Utiliser Terraform ou OpenTofu pour créer des VM sur Proxmox

Monter son environnement grâce à du code

Utiliser Terraform ou OpenTofu pour créer des VM sur Proxmox

Suite à un article portant sur l'utilisation de Terraform pour créer des LXC pour Proxmox, je souhaite approfondir le sujet en abordant cette fois la création de machines virtuelles.

L'objectif de ce document est de guider le déploiement de machines virtuelles sur un hôte Proxmox à partir d'un modèle créé à l'aide de Packer. Pour étendre ces déploiements à une plus grande échelle, il suffira de dupliquer les blocs de code et d'ajuster les valeurs en conséquence.

Prérequis : Une connaissance préalable de Terraform, des droits d'administration sur Proxmox, et la capacité à effectuer des manipulations sans contraintes majeures. J'utilise également OpenTofu, compatible avec Terraform sans modification de code au moment de la rédaction de cet article.

L'environnement de travail pour cet article est le suivant :

  • un serveur Proxmox 8.1.x
  • un éditeur de code
  • Terraform 1.6.x ou Open Tofu 1.6.x

Préparation de l'environnement de travail

Avant de commencer le déploiement automatisé avec Terraform ou OpenTofu, assurez-vous que votre environnement est correctement configuré en suivant ces étapes :

  1. Installation de Terraform ou OpenTofu :
    1. Dans un terminal, procédez à l'installation de Terraform en suivant les indications de la documentation officielle d'HashiCorp, adaptées à votre système d'exploitation. De même, pour OpenTofu, référez-vous à ce lien de documentation pour obtenir toutes les informations nécessaires. Assurez-vous que Terraform ou OpenTofu est correctement installé en vérifiant la version : $ terraform version.
  2. Accès à Proxmox :
    1. Assurez-vous d'avoir un accès à votre hôte Proxmox via un navigateur web.
    2. Vérifiez aussi l'accès via SSH pour des opérations en ligne de commande.
    3. Identifiants pour Proxmox : Disposez des identifiants de l'utilisateur "root" ou d'un autre utilisateur ayant des privilèges élevés sur Proxmox pour créer des machines virtuelles et gérer le stockage.

Avec ces prérequis en place, vous êtes prêt à automatiser le déploiement de machines virtuelles avec Terraform ou OpenTofu.

Préparation de Proxmox

Pour simplifier les opérations dans vos environnements de test et raccourcir ce document, vous pouvez utiliser le compte root@pam créé lors de l'installation de Proxmox. Ce compte possède tous les privilèges. Pour une gestion plus fine, la création d'un token pour le compte root est conseillée.

Le code Terraform complet

Enfin, voici le code complet que j'utilise pour créer des VM sur Proxmox avec Terraform ou OpenTofu. Le code est assez complet, j'aime bien avoir le maximum d'informations et d'attributs pour préciser au maximum mes ressources.

Fichier "provider.tf" :

### provider.tf
terraform {
  required_providers {
    proxmox = {
      source  = "bpg/proxmox"
      version = ">=0.41.0"
    }
  }
}

provider "proxmox" {
  api_token = var.pve_api_token
  endpoint  = var.pve_host_address
  insecure  = true
  tmp_dir   = var.tmp_dir
}

Fichier "variables.tf" :

### variables.tf
variable "clone_node_name" {
  type = string
}
variable "clone_vm_id" {
  type = number
}
variable "cloudinit_dns_domain" {
  type = string
}
variable "cloudinit_dns_servers" {
  type = list(string)
}
variable "cloudinit_ssh_keys" {
  type = list(string)
}
variable "cloudinit_user_account" {
  type = string
}
variable "datastore_id" {
  type = string
}
variable "disk_file_format" {
  type = string
}
variable "node_name" {
  type = string
}
variable "pve_api_token" {
  type = string
}
variable "pve_host_address" {
  type = string
}
variable "tmp_dir" {
  type = string
}
variable "vm_bridge_lan" {
  type = string
}
variable "vm_cpu_cores_number" {
  type = number
}
variable "vm_cpu_type" {
  type = string
}
variable "vm_description" {
  type = string
}
variable "vm_disk_size" {
  type = number
}
variable "vm_id" {
  type = number
}
variable "vm_memory_max" {
  type = number
}
variable "vm_memory_min" {
  type = number
}
variable "vm_name" {
  type = string
}
variable "vm_socket_number" {
  type = number
}

Fichier "variables.auto.tfvars", c'est ce fichier qu'il faut modifier pour que vous puissiez avoir un conteneur correspondant à votre besoin :

### variables.auto.tfvars
clone_node_name        = "pve_host"
clone_vm_id            = 99999
cloudinit_dns_domain   = "your.domain.net"
cloudinit_dns_servers  = ["9.9.9.9"]
cloudinit_ssh_keys     = ["ssh-ed25519 changeme"]
cloudinit_user_account = "jho"
datastore_id           = "local"
disk_file_format       = "raw"
node_name              = "pve"
pve_api_token          = "terrabot@pve!token_name=token_secret"
pve_host_address       = "https://pve:8006"
tmp_dir                = "/tmp"
vm_bridge_lan          = "vmbr0"
vm_cpu_cores_number    = 2
vm_cpu_type            = "x86-64-v2-AES"
vm_description         = "Managed by terraform."
vm_disk_size           = 64
vm_id                  = 9993
vm_memory_max          = 8192
vm_memory_min          = 4096
vm_name                = "tf_example"
vm_socket_number       = 1

Fichier "vm.tf" : J'utilise Cloud-Init pour faciliter la configuration de la machine au niveau réseau. Si vous ne souhaitez pas utiliser Cloud-Init, supprimez le bloc "{ initialization }" :

### vm.tf

resource "proxmox_virtual_environment_vm" "vm1_example" {
  description     = var.vm_description
  keyboard_layout = "fr"
  machine         = "q35"
  migrate         = true
  name            = var.vm_name
  node_name       = var.node_name
  scsi_hardware   = "virtio-scsi-single"
  started         = true
  tablet_device   = false
  vm_id           = var.vm_id

  clone {
    datastore_id = var.datastore_id
    full         = true
    node_name    = var.clone_node_name
    retries      = 2
    vm_id        = var.clone_vm_id
  }

  agent {
    enabled = true
    trim    = true
  }

  cpu {
    cores   = var.vm_cpu_cores_number
    numa    = true
    sockets = var.vm_socket_number
    type    = var.vm_cpu_type
  }

  disk {
    datastore_id = var.datastore_id
    discard      = "on"
    file_format  = var.disk_file_format
    interface    = "scsi0"
    iothread     = true
    size         = var.vm_disk_size
  }

  initialization {
    datastore_id = var.datastore_id
    dns {
      domain  = var.cloudinit_dns_domain
      servers = var.cloudinit_dns_servers
    }
    ip_config {
      ipv4 {
        address = "172.16.241.10/16"
        gateway = "172.16.0.1"
      }
    }
    user_account {
      keys     = var.cloudinit_ssh_keys
      username = var.cloudinit_user_account
    }
  }

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

  network_device {
    bridge = var.vm_bridge_lan
    model  = "virtio"
  }

  operating_system {
    type = "l26"
  }

  startup {
    order      = "1"
    up_delay   = "20"
    down_delay = "20"
  }

  serial_device {}
}

Dans ces fichiers, des valeurs sont à modifier et doivent correspondre à vos besoins. Relisez bien chaque fichier et adaptez-les selon votre infrastructure. J'ai maximisé l'utilisation de variables pour faciliter les ajustements.