Aller au contenu

Terraform avec Proxmox PVE

Terraform est un outil puissant d'infrastructure as code développé par HashiCorp. Il permet de définir et de provisionner l'infrastructure de manière déclarative, en utilisant un langage de configuration, le HCL. Grâce à Terraform, les équipes peuvent gérer de manière cohérente et automatisée des ressources cloud, sur site ou hybrides, en réduisant les erreurs manuelles et en accélérant le déploiement.

Dans cet article, je fais la démonstration de l'utilisation de Terraform avec Proxmox en exploitant du cloudinit, les VLAN, spécifications de la carte réseau ainsi que l'emplacement du datastore.

img

Le code Terraform : ici

Important : J'utilise deux datastores (NAS & DATASTORE) à la place de local et local-lvm.

Prérequis

Création du rôle

Proxmox permet de créer des rôles personnalisés avec des permissions spécifiques. Utilisez la commande suivante, en vous connectant en ssh auparavant, pour créer un nouveau rôle (par exemple, TerraformRole) avec des permissions adaptées à Terraform :

pveum role add TerraformProv -privs "Datastore.Allocate Datastore.AllocateSpace 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.Console VM.Migrate VM.Monitor VM.PowerMgmt SDN.Use"

Cette commande crée un rôle TerraformRole avec des privilèges adaptés pour gérer les machines virtuelles.

Création de l'utilisateur

Créez un utilisateur spécifique pour Terraform. Par exemple, pour créer un utilisateur terraform-prov dans le domaine @pve avec un mot de passe, utilisez :

pveum user add terraform-prov@pve --password password1234

Après avoir créé l’utilisateur, attribuez-lui le rôle créé précédemment. Vous pouvez le faire pour l’ensemble du datacenter :

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

Cette commande assigne le rôle TerraformRole à terraform-prov@pve pour toutes les ressources du datacenter.

Génération d'un token

Pour une intégration sécurisée et efficace entre Terraform et Proxmox, l’utilisation d’un token API est recommandée. Cela permet à Terraform de se connecter et d’interagir avec Proxmox sans nécessiter des informations d’identification utilisateur classiques. Voici la procédure pour créer un token API dans Proxmox et le configurer dans Terraform.

pveum user token add terraform-prov@pve terraform -expire 0 -privsep 0 -comment "Terraform token"

Création des ressources

L'arborescence cible :

./
├── credentials.auto-sample.tfvars
├── credentials.auto.tfvars
├── main.tf
├── modules
│   ├── debian-bookworm
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── providers.tf
│   │   └── variables.tf
│   └── debian-bookworm-datastore
│       ├── main.tf
│       ├── outputs.tf
│       ├── providers.tf
│       └── variables.tf
├── providers.tf
├── README.md
├── terraform.tfstate
├── terraform.tfstate.backup
└── variables.tf

Notre module sera situé dans le répertoire : /modules/debian-bookworm/. Il sera composé des fichiers suivants :

  • main.tf : Ressources principales
  • variables.tf : Variables d’entrée
  • outputs.tf : Valeurs de sortie
  • providers.tf : Déclaration du provider Proxmox

Création de templates

Je t'invite à lire l'article suivant : cloud-init

Déclaration du Provider

Dans le fichier providers.tf

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

provider "proxmox" {
  endpoint  = var.endpoint
  api_token = var.api_token
  username  = var.username
  password  = var.password
  insecure  = true
  ssh {
    agent    = true
    username = "root"
  }
}

Dans le fichier credentials.auto.tfvars

endpoint  = "https://PROMXO-IP:8006/"
username  = "terraform-prov@pve"
password  = "adminuser"
api_token = "something"

Dans le fichier variables.tf

variable "endpoint" {
  description = "URL vers Proxmox"
  type = string
}

variable "username" {
  description = "Login compte technique"
  type = string
}

variable "password" {
  description = "Password compte technique"
  type = string
}

variable "api_token" {
  description = "Token to connect Proxmox API"
  type = string
}

Déclaration de notre premier module

Créer le fichier suivant /modules/debian-bookworm/main.tf.

Le fichier main.tf contient deux ressources :

  1. Téléchargement de l’image Debian Bookworm au format qcow2 depuis le dépôt officiel.
  2. Création de la VM à partir de cette image avec toutes les options configurables (CPU, mémoire, réseau, disques, etc.).
resource "proxmox_virtual_environment_download_file" "latest_debian_12_bookworm_qcow2_img" {
  content_type = "iso"
  datastore_id = var.datastore_id
  file_name    = "debian-12-generic-amd64.qcow2.img"
  node_name    = var.node_name
  url          = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2"
}

resource "proxmox_virtual_environment_vm" "debian_template" {
  description = "Managed by Terraform"
  vm_id       = var.vm_id
  name        = var.vm_name
  node_name   = var.node_name
  tags        = var.tags

  on_boot  = false
  template = false
  started  = true

  cpu {
    cores = var.cpu_cores
    type  = var.cpu_type
  }

  agent {
    enabled = true
    timeout = "2m"
  }

  startup {
    order      = "3"
    up_delay   = "60"
    down_delay = "60"
  }

  memory {
    dedicated = var.memory
    floating  = var.memory
  }

  disk {
    interface    = "scsi0"
    datastore_id = var.datastore_id
    file_id      = proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img.id
    size         = var.disk_size
    discard      = "on"
    ssd          = false
  }

  initialization {
    datastore_id = var.datastore_id
    ip_config {
      ipv4 {
        address = var.ipv4_address
        gateway = var.gateway
      }
    }

    dns {
      servers = var.dns_servers
    }

    user_account {
      username = var.username
      keys     = [file(var.ssh_public_key_path)]
    }
  }

  network_device {
    bridge   = var.network_bridge
    model    = "virtio"
    firewall = false
    enabled  = true
    vlan_id = var.network_vlan_id
  }

  operating_system {
    type = "l26"
  }

  tpm_state {
    datastore_id = var.datastore_id
    version = "v2.0"
  }

  serial_device {
    device = "socket"
  }
}

La VM inclut :

  • Un agent QEMU activé pour la communication avec Proxmox
  • Une configuration réseau statique
  • Une clé SSH injectée pour l’accès sans mot de passe
  • Un support TPM 2.0 pour la sécurité
  • Des délais de démarrage personnalisés
  • Une interface réseau virtio avec support VLAN

Créer le fichier suivant /modules/debian-bookworm/variables.tf.

Ce fichier rend le module flexible et personnalisable. Vous pouvez définir :

  • L’ID et le nom de la VM
  • Le type et nombre de CPU
  • La mémoire et la taille du disque
  • Les paramètres réseau (IP, passerelle, DNS, bridge, VLAN)
  • Le nom du noeud Proxmox
  • Le chemin vers la clé SSH publique
variable "vm_id" {
  description = "ID unique de la VM dans le cluster Proxmox"
  type        = number
}

variable "vm_name" {
  description = "Nom de la VM"
  type        = string
  default     = "debian12-template"
}

variable "node_name" {
  description = "Nom du noeud Proxmox"
  type        = string
}

variable "tags" {
  description = "Tags de la VM"
  type        = list(string)
  default     = ["terraform", "debian12", "template"]
}

variable "cpu_cores" {
  description = "Nombre de coeurs CPU"
  type        = number
  default     = 2
}

variable "cpu_type" {
  description = "Type de CPU"
  type        = string
  default     = "x86-64-v2-AES"
}

variable "memory" {
  description = "Mémoire en Mo"
  type        = number
  default     = 1024
}

variable "disk_size" {
  description = "Taille du disque en Go"
  type        = number
  default     = 10
}

variable "datastore_id" {
  description = "ID du datastore pour le disque"
  type        = string
}

variable "ipv4_address" {
  description = "Adresse IPv4 statique"
  type        = string
}

variable "gateway" {
  description = "Passerelle par défaut"
  type        = string
}

variable "dns_servers" {
  description = "Liste des serveurs DNS"
  type        = list(string)
  default     = ["8.8.1.1", "8.8.8.8"]
}

variable "username" {
  description = "Nom d'utilisateur pour la VM"
  type        = string
  default     = "cloud"
}

variable "ssh_public_key_path" {
  description = "Chemin du fichier contenant la clé publique SSH"
  type        = string
}

variable "network_bridge" {
  description = "Nom du pont réseau"
  type        = string
  default     = "vmbr1"
}

variable "network_vlan_id" {
  description = "Tag VLAN à appliquer à la carte réseau (optionnel)"
  type        = number
  default     = 20
}

Créer le fichier suivant /modules/debian-bookworm/outputs.tf

Après l’application du module, vous pouvez récupérer facilement certaines informations utiles :

output "vm_ip" {
  value       = proxmox_virtual_environment_vm.debian_template.initialization[0].ip_config[0].ipv4[0].address
  description = "Adresse IP de la VM Debian"
}

output "ssh_public_key_path" {
  value = var.ssh_public_key_path
}

Cela permet par exemple d’enchaîner automatiquement avec une configuration Ansible ou un test de connectivité.

Créer le fichier suivant /modules/debian-bookworm/providers.tf.

Nous déclarons ici le provider Proxmox requis :

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

Le provider bpg/proxmox est un fork communautaire largement utilisé.

Déclaration de notre second module

La seule différence est qu'on copie l'image pour créer la VM. Ça évite de télécharger pour chaque VM depuis le WEB. Dans le main.tfdu module au niveau de :

  disk {
    interface    = "scsi0"
    datastore_id = var.datastore_id
    file_id      = proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img.id
    size         = var.disk_size
    discard      = "on"
    ssd          = false
  }

Devient :

  disk {
    datastore_id = var.datastore_id
    file_id      = "NAS:iso/debian-12-generic-amd64.img"
    interface    = "virtio0"
    iothread     = true
    discard      = "on"
    size         = var.disk_size
  }

NAS: À remplacer par le nom du datastore qui contient l'image.

Exemple d’utilisation

Vous pouvez intégrer ce module dans un projet Terraform plus large comme suit :

module "debian_bookworm" {
  source             = "./modules/debian-bookworm"
  vm_id              = 101
  node_name          = "pve1"
  datastore_id       = "local-lvm"
  ipv4_address       = "192.168.1.101/24"
  gateway            = "192.168.1.1"
  ssh_public_key_path = "~/.ssh/id_rsa.pub"
}

Uniquement à titre d'exemple.

Exemple d’utilisation avec loop

locals {
  debian_vms = {
    debian-vm-01 = { vm_id = 501, ip = "172.16.20.40/24" }
    debian-vm-02 = { vm_id = 502, ip = "172.16.20.41/24" }
    debian-vm-03 = { vm_id = 503, ip = "172.16.20.42/24" }
    debian-vm-04 = { vm_id = 504, ip = "172.16.20.43/24" }
    debian-vm-05 = { vm_id = 505, ip = "172.16.20.44/24" }
    debian-vm-06 = { vm_id = 506, ip = "172.16.20.45/24" }
  }
}

module "debian_vm" {
  for_each            = local.debian_vms

  source              = "./modules/debian-bookworm-datastore"
  vm_id               = each.value.vm_id
  vm_name             = each.key
  node_name           = "pve"
  datastore_id        = "DATASTORE"
  disk_size           = 20
  ipv4_address        = each.value.ip
  gateway             = "172.16.20.254"
  ssh_public_key_path = "~/.ssh/id_rsa.pub"
}

Définit un ensemble de VM Debian à déployer.

Chaque entrée correspond à une VM avec :

  • vm_id : un identifiant unique pour la VM.
  • ip : l’adresse IP avec masque CIDR.

Paramètres du module :

  • source : chemin vers le module Terraform local.
  • for_each : instancie une ressource pour chaque élément défini dans local.debian_vms.

Variables passées au module :

  • vm_id : identifiant de la VM.
  • vm_name : nom de la VM, basé sur la clé de la map.
  • node_name : nom du noeud Proxmox cible.
  • datastore_id : identifiant du stockage cible.
  • disk_size : taille du disque de la VM en Go.
  • ipv4_address : adresse IP attribuée à la VM.
  • gateway : passerelle réseau commune à toutes les VMs.
  • ssh_public_key_path : chemin vers la clé SSH publique à injecter.

Résultat

res

res