Imad’s wiki
GithubLinkedInXMailHashnode
  • ADMINISTRATION LINUX
    • Linux et ses distributions
    • Commandes Utiles
    • Gestion des paquets
    • SSH
    • Partitionnement LVM
  • Base de données
    • PostgreSQL
      • Quelques commandes Postgres
      • Mise à jour de Postgresql sous windows
  • Tutoriels
    • Securité
      • Wazuh
      • Intégration Debian sur Active Directory
      • Certificat SSL
    • SingleSignOn
      • Authentik
      • SAML + CENTREON
      • OpenID + Apache Guacamole
      • OpenID + Proxmox
    • Git
      • Git
    • Infra-As-Code
      • Terraform + Proxmox
      • Ansible
    • Conteneurisation
      • Docker
        • Quelques commandes Docker utiles
Powered by GitBook
On this page
  • Prérequis
  • Création du rôle et de l'utilisateur Proxmox pour Terraform (via CLI)
  • Configuration des variables d’environnement
  • Création du template cloudinit:
  • Création d'une clé ssh
  • Création des fichiers de configs terraform
  • Déploiement

Was this helpful?

  1. Tutoriels
  2. Infra-As-Code

Terraform + Proxmox

Déploiement de VMs sur proxmox avec terraform à partir d'un template CloudInit

PreviousInfra-As-CodeNextAnsible

Last updated 6 days ago

Was this helpful?

Cette doc permet le déploiement automatisé de machines virtuelles (VM) sur un cluster Proxmox VE à l’aide de Terraform et du provider . Bonus :

Prérequis

  • Un serveur Proxmox VE en version 8.x

  • Une machine Linux avec Terraform (>= 1.5) ou OpenTofu (>= 1.6) installé

  • Un utilisateur dédié sur Proxmox avec un API Token configuré

  • Un templaye de vm

  • Les accès SSH configurés si nécessaire pour la provision des VM

Création du rôle et de l'utilisateur Proxmox pour Terraform (via CLI)

Docs officielle :

Créer un rôle Terraform avec les droits nécessaires

pveum role add TerraformProv -privs "Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit Pool.Allocate Sys.Audit Sys.Console Sys.Modify VM.Allocate VM.Audit VM.Clone VM.Config.CDROM VM.Config.Cloudinit VM.Config.CPU VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Migrate VM.Monitor VM.PowerMgmt SDN.Use"

Ce rôle regroupe toutes les permissions recommandées pour piloter des VM, disques, réseaux, cloud-init, migration, etc.

Créer l'utilisateur Terraform

pveum user add terraform-prov@pve --password <votre_mot_de_passe>

Remplacer <votre_mot_de_passe> par un mot de passe fort.

Associer le rôle à l'utilisateur sur tout le cluster

pveum aclmod / -user terraform-prov@pve -role TerraformProv

Le / cible l’ensemble du cluster, ajustez si besoin pour limiter à un nœud ou un pool spécifique.

Créer un token API pour l’utilisateur

pveum user token add terraform-prov@pve terraform -expire 0 -privsep 0 -comment "Terraform token"
  • -expire 0 : pas d’expiration automatique

  • -privsep 0 : désactive la séparation de privilèges, nécessaire pour Terraform

  • Notez immédiatement le secret du token affiché : il ne sera plus visible ensuite.

Génération du Token API

Créer un token API pour l’utilisateur (ici nommé terraform) et accorder les roles necessaire au token :

pveum user token add terraform-prov@pve terraform -comment "Token Terraform"
#pveum aclmod / --roles TerraformProv --token 'terraform@pve!terraform' --propagate 1
  • Notez bien le token secret affiché : il ne sera plus visible ensuite.

Configuration des variables d’environnement

Ajouter ces variables à votre shell avant d’utiliser Terraform  :

A faire apres chaque fermerture de la session

export PROXMOX_VE_ENDPOINT="https://proxmox.lab.prod:8006/"
export PROXMOX_VE_INSECURE=true
export PROXMOX_VE_USERNAME="terraform-uer-proxmox@pve"
export PROXMOX_VE_PASSWORD="user-proxmox-x"
export PROXMOX_VE_API_TOKEN="API_Token"

Création du template cloudinit:

  • ID : 9000

  • nom de la VM : debian-cloudinit

  • RAM : 2Go

  • Interface réseau : vmbr1

✅1. Télécharger une ISO Debian Cloud-Init Ready

Utiliser une ISO minimale, par exemple via netinst, ou directement une image cloud-init-ready comme :

wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2

✅ 2. Créer une VM dans Proxmox (sans ISO) en CLI

qm create 9000 --name debian-cloudinit --memory 2048 --net0 virtio,bridge=vmbr1

✅ 3. Importer l’image disque téléchargée

qm importdisk 9000 debian-12-genericcloud-amd64.qcow2 local

Où local représente le nom du Datastore. Ensuite, attacher le disque au contrôleur :

qm set 9000 --scsihw virtio-scsi-pci --scsi0 local:9000/vm-9000-disk-0.raw

✅ 4. Ajouter le disque Cloud-Init

Sur Proxmox, le disque Cloud-Init est un volume virtuel ajouté à une machine virtuelle pour automatiser sa configuration lors du premier démarrage. Ce disque contient les paramètres essentiels définis par l’administrateur, tels que le nom d’hôte, la configuration réseau, les utilisateurs, les mots de passe ou encore les clés SSH. Lors du boot initial, l’agent Cloud-Init présent dans l’image de la VM détecte ce disque et applique automatiquement toutes les configurations fournies, ce qui permet de déployer rapidement et de façon standardisée des machines virtuelles personnalisées sans intervention manuelle répétitive. Cette méthode est particulièrement utile pour le clonage de templates, le déploiement en masse ou l’intégration à des infrastructures cloud, car elle garantit que chaque VM démarre avec les bons paramètres dès la première utilisation.

qm set 9000 --ide2 local:cloudinit

✅ 5. Configurer le boot & autres options

qm set 9000 --boot c --bootdisk scsi0
qm set 9000 --serial0 socket --vga serial0

✅ 6. Convertir en template

qm template 9000

🔁 Ensuite dans Terraform

On pourra directement l’utiliser comme vm_id = 9000 dans ta config clone.

🧼 Bonus : nettoyage au besoin

rm debian-12-genericcloud-amd64.qcow2

Création d'une clé ssh

Cette clé ssh sera déployée sur les vms qu'on créera

ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa

Cela génèrera deux fichiers :

  • id_rsa → Clé privée (à garder secrète).

  • id_rsa.pub → Clé publique (à injecter dans les VMs).

Place la clé publique dans le dossier Terraform : Le fichier id_rsa.pub doit se trouver dans le dossier contenant ton projet Terraform.

Création des fichiers de configs terraform

main.tf

Ce fichier initialise le projet Terraform et définit le provider principal utilisé : bpg/proxmox (version 0.66.2). Cette configuration est nécessaire pour permettre à Terraform d’interagir avec l’API Proxmox.

terraform {
  required_providers {
    proxmox = {
      source  = "bpg/proxmox"
      version = "0.66.2"
    }
  }
}

data.tf

Ce fichier permet à terrafom de récupérer une clé publique en data source et de lire le contenu du fichier id_rsa.pub et de l’utiliser dans la configuration via data.local_file.ssh_public_key.content C’est la méthode recommandée pour importer une clé publique locale dans Terraform

data "local_file" "ssh_public_key" {
  filename = "${path.module}/id_rsa.pub"
}
  • local_file est un data source qui lit le contenu d’un fichier local.

  • trimspace(...) est utilisé ensuite dans le vm.tf pour s'assurer qu'aucun saut de ligne inutile ne soit injecté dans le cloud-init.

Vous pouvez remplacer ${path.module} par le chemin où se trouve la clé. Moi je l'ai dans le répertoire de mon projet. donc mon data.tf devient :

data "local_file" "ssh_public_key" {
filename = ".id_rsa_pub_deploy"
}

variables.tf

Ce fichier définit l’ensemble des variables d’entrée utilisées dans le projet Terraform pour la création de machines virtuelles sur Proxmox. Modifiez ces variables selon votre environnement et vos besoins.

variable "node_name" {
  description = "proxmox "
  default     = "prox"
}

variable "template_debian_id" {
  description = " template debian cloudinit"
  default     = "9000"
}

variable "nb_vms" {
  description = "nombre de vms à créer"
}

variable "vm_names" {
  description = "nom des vm"
  type        = list(string)
  default     = ["vm1", "vm2", "vm3"]
}

Le bloc suivant :

variable "vm_names" {
  description = "nom des vm"
  type        = list(string)
  default     = ["vm1", "vm2", "vm3"]
}

définit une variable appelée vm_names, qui est une liste de chaînes de caractères (donc un tableau de noms). Chaque élément de cette liste représente le nom d'une VM à créer avec Terraform.

Par exemple :

for_each = toset(var.vm_names)
name     = each.key

Cela permet de créer une VM pour chaque nom fourni dans la liste, avec des paramètres propres (nom, IP, ID, etc.).

⚠️ Important : cette liste reflète l’état réel des VMs gérées par Terraform. Si on supprime un nom de la liste, Terraform considérera que la VM correspondante doit être détruite. Donc, pour ajouter de nouvelles VMs sans toucher aux existantes, il faut ajouter les nouveaux noms à la liste sans retirer les anciens

outputs.tf

Ce fichier définit les sorties (outputs) de Terraform pour ce projet. Ici, on expose les adresses IPv4 de toutes les VMs créées, ce qui permet de les retrouver facilement après un apply.

output "vms_ipv4" {
  description = "IPv4 des VMs créées"
  value = {
    for vm_name, vm in proxmox_virtual_environment_vm.vm :
    vm_name => vm.ipv4_addresses
  }
}

vm.tf

Ce fichier définit la ressource principale de machine virtuelle (VM) à déployer sur Proxmox via Terraform. Il s’appuie sur les variables du projet et la clé publique SSH importée en data source.

Dans Terraform, le mot de passe fourni à user_account doit être hashé .

  • Génération du mot de passe chiffré

sudo apt install openssl
sudo dnf install openssl # pour le gestionnaire de paquet dnf
sudo yum install openssl # pour le gestionnaire de paquet yum
openssl passwd -6 le-mot-de-passe-à-chiffré
  • Exemple de résultat

imad@:/mnt/c/Users/imad$ openssl passwd -6 password
$6$tHVVDVCWxyr0H0kf$AXm7J6O/nGKMpKK1qzx/uMMRKw7IawWPHWbhu0lYIkql/I/chCaX9nAdisUJ65tuY1RBb4WqqqNMErQiE725n0

Le mot de passe haché est à utiliser dans :

user_account {
  username = "imad"
  password = "<mot_de_passe_hashé>"
  keys     = [trimspace(data.local_file.ssh_public_key.content)]
}

Le fichier vm.tf devrait ressembler à ca

resource "proxmox_virtual_environment_vm" "vm" {
  for_each  = toset(var.vm_names)
  name      = each.key
  node_name = var.node_name
  vm_id     = 900 + index(var.vm_names, each.key)
  clone {
    vm_id = 9000
    full  = true
  }
  initialization {
    datastore_id = "local"

    user_account {
      username = "imad"
      password = "mot-de-passe-haché"
      keys     = [trimspace(data.local_file.ssh_public_key.content)]
    }
    dns {
      domain  = "local"
      servers = ["1.1.1.1"]
    }
    ip_config {
      ipv4 {
        address = "10.10.10.${50 + index(var.vm_names, each.key)}/24"
        gateway = "10.10.10.1"
      }
    }
  }
  lifecycle {
    ignore_changes = [
      # ignore toute mise à jour sur ces attributs pour les VMs existantes
      initialization[0].ip_config[0].ipv4[0].address,
      initialization[0].ip_config[0].ipv4[0].gateway,
      network_device,
      disk,
      memory,
      cpu
    ]
  }
  #  prevent_destroy = true
}

Pourquoi for_each ?

Contrairement à count, for_each permet de :

  • Nommer chaque ressource individuellement (via une clé explicite),

  • Ajouter/retirer des ressources sans destruction des autres,

  • Gérer une configuration plus flexible.

Exemple :

variable "vm_names" {
  default = ["vm1", "vm2", "vm3"]
}

resource "proxmox_virtual_environment_vm" "vm" {
  for_each  = toset(var.vm_names)
  name      = each.key

Ce que ça fait :

  • Crée une VM nommée vm1, une autre vm2, etc.

  • Chaque VM est traitée indépendamment.


Génération automatique des vm_id et adresses IP

🔢 vm_id unique

vm_id = 900 + index(var.vm_names, each.key)

→ Cela génère :

  • vm1 → 900

  • vm2 → 901

  • vm3 → 902

🌐 IPs fixes

address = "10.10.10.${50 + index(var.vm_names, each.key)}/24"

→ IPs attribuées :

  • bastion → 10.10.10.50

  • kube-master → 10.10.10.51

  • kube-worker → 10.10.10.52


🔄 Utilisation de lifecycle.ignore_changes

lifecycle {
  ignore_changes = [
    initialization[0].ip_config[0].ipv4[0].address,
    initialization[0].ip_config[0].ipv4[0].gateway,
    network_device,
    memory,
    cpu,
    disk
  ]
}

Pouvoir modifier manuellement ces paramètres (RAM, CPU, IP, etc.) via l’interface Proxmox sans que Terraform essaie de les remettre à leur état initial à chaque apply.

Prevent destroy

Le champ prevent_destroy dans Terraform est une protection qui empêche la suppression accidentelle d'une ressource, même si elle n’existe plus dans le code ou si tu fais un terraform destroy.

 prevent_destroy = true

Déploiement

  • terraform init → Prépare l’environnement

  • terraform fmt → Formate le code

  • terraform validate → Vérifie la validité de la configuration

  • terraform plan → Prévisualise les changements

  • terraform apply → Applique les changements sur l’infrastructure

Pour ajouter de nouvelles VMs à défaut de modifier le fichier variables.tf :

terraform apply -var='vm_names=["vm1", "vm2", "vm3", "dns"]'

proxmox-bpg-terraform/ ├── README.md ├── main.tf ├── data.tf ├── variables.tf ├── outputs.tf ├── vm.tf Tout ça est disponible sur mon

Ce format est essentiel car il permet d'utiliser for_each dans le fichier , ce qui offre un meilleur contrôle sur chaque ressource individuellement (contrairement à count, qui fonctionne avec des index numériques sans lien direct avec le nom).

Avec keys = [trimspace(data.local_file.ssh_public_key.content)] qui représente la clé public récupéré en data source dans et username le nom d'utilisateur des vms qui seront déployés

Une fois les Vms créées on peut leur appliquer des configs spécifiques avec ansible.

bpg/proxmox
Petite configuration Ansible
https://registry.terraform.io/providers/bpg/proxmox/latest/docs#api-token-authentication
repo Github
ICI
vm.tf
data.tf
Page cover image