L'objectif est de créer une VM pour chaque membre d'un groupe de 37 personnes:
Etape 1
cd /var/lib/vz/template/iso
wget https://cdimage.debian.org/cdimage/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2
cp -pr debian-12-genericcloud-amd64.qcow2 debian-12-genericcloud-amd64-v2.qcow2 # on crée une copie pour disposer d'une image "propre" en cas de problème.
virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --install qemu-guest-agent
virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --root-password password:******
Dès le départ, on peut profiter de la création de cette image de base pour implémenter certaines fonctionnalités que l'on souhaite retrouver sur toutes les VM (un dossier, des utilitaires...)
virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --mkdir /home/toto
virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --install vim,bash-completion,wget,curl,telnet,unzip,net-tools
Important :
Les déploiements via Ansible impliquent de pouvoir se connecter en ssh sur toutes les VM sans utiliser le compte root : il faut donc créer un utilisateur commun à toutes les VM qui soit autorisé à se connecter en SSH. Je n'ai pas trouvé d'autre moyen de le faire qu'au moment de la création de l'image. Cela implique également une modification de la configuration par défaut de SSH qui crée une "faille" de sécurité (on autorise la connexion par mot de passe pour un utilisateur.) Si quelqu'un a une alternative, je suis preneur.
- a- Création d'un utilisateur "ansible"
- b- Ajout de l'utilisateur "ansible" au groupe des sudoers
- c- Modification du fichier
/etc/ssh/sshd_configpour permettre l'authentification par mot de passe et autoriser l'utilisateur "ansible". Soit:virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --run-command 'useradd ansible --password ****** -s /bin/bash' ## NB: il faut obligatoirement crypter le mot de passe (avec openssl) virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --run-command "sudo usermod -aG sudo ansible" virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --run-command 'sed -i "/PasswordAuthentication no/d" /etc/ssh/sshd_config' virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --run-command 'echo PasswordAuthentication yes >> /etc/ssh/sshd_config' virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --run-command 'sed -i "/PasswordAuthentication no/d" /etc/ssh/sshd_config' virt-customize -a debian-12-genericcloud-amd64-v2.qcow2 --run-command 'echo AllowUsers ansible >> /etc/ssh/sshd_config'
En veillant à rester ou revenir dans le répertoire où se trouve l'image debian modifiée (/var/lib/vz/template/iso) on procède à la création et au paramétrage de la VM qui va servir de modèle/template:
qm create 1008 --memory 2048 --core 2 --name debian12-custom --net0 virtio,bridge=vmbr1 --description "Debian 12 personnalisée"
qm importdisk 1008 debian-12-genericcloud-amd64-v2.qcow2 <storage>
qm set 1008 --scsihw virtio-scsi-pci --scsi0 <storage>:1008/vm-1008-disk-0.raw
qm set 1008 --boot c --bootdisk scsi0
qm set 1008 --ide2 <storage>:cloudinit
qm set 1008 --serial0 socket --vga qxl #ou serial0
qm set 1008 --agent enabled=1
qm template 1008
Cette VM est la base qui sera ensuite clonée autant de fois que nécessaire (ici, 37) puis modifiée par la suite avec Ansible.
Pour l'installation, se reporter à la documentation officielle.
Dans cet exemple, c'est le provider bpg qui a été utilisé (dans sa version 0.46.1, au moment de la rédaction).
Création de l'utilisateur terraform
pveum user add terraform@pve --password <password>
Il faut que cet utilisateur ait des droits de création, de configuration et d'attribution de ressources (à affiner et compléter en fonction de ce qui est déployé sur le serveur Proxmox)
Création d'un rôle "Terraform" spécifique sur le serveur Proxmox.
pveum role add Terraform -privs "VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Monitor VM.Audit VM.PowerMgmt Datastore.AllocateSpace Datastore.Audit"
Attribution du rôle "Terraform" à l'utilisateur terraform
pveum aclmod / -user terraform@pve -role Terraform
Génération du token/jeton de connexion (value générée à noter précieusement)
pveum user token add terraform@pve terraform -expire 0 -privsep 0 -comment "jeton pour terraform"
Préalable : Depuis le machine utilisée pour le déploiement, on doit pouvoir se connecter avec une clé ssh au serveur Proxmox avec ssh-agent.
Trois fichiers sont à créer dans le même répertoire (voir dépôt):
Le fichier main.tf est assez simple dans sa définition. C'est grâce à csvdecode qu'il pourra générer autant de VM que d'utilisateurs présents dans le csv en implémentant dans chaque VM des données uniques (nom, ip, username, password, VMid).
main.tf
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.46.1"
}
}
}
provider "proxmox" {
endpoint = "https://server_proxmox:8006/"
api_token = var.api_token # c'est une variable saisie dans le fichier variables.tf
insecure = true # dans le cas d'un certificat auto-signé
ssh {
agent = true
username = "root"
}
}
locals {
users = csvdecode(file("utilisateurs.csv")) # csvdecode -propre à terraform- va permettre d'extraire du fichier csv des utilisateurs les données username, password, IP et VMID
users_map = { for u in local.users : u.username => u }
}
resource "proxmox_virtual_environment_vm" "vm" {
for_each = local.users_map
name = each.value.username # le nom de chaque VM est tiré du csv.
# Nom de machine = nom d'utilisateur (utile pour la suite)
vm_id = each.value.vmid # idem pour le numéro à lui attribuer dans Prowmox
node_name = var.node # le nom du noeud proxmox
on_boot = var.boot_mode # démarrer les VM au boot
started = var.started
agent {
enabled = true
}
startup {
order = "3" # ordre de démarrage VM (à ajuster selon les situations)
up_delay = "10" # délai avant démarrage VM suivante (à ajuster selon les situations)
down_delay = "10" # délai avant extinction VM suivante (à ajuster selon les situations)
}
clone {
vm_id = 1008 # Le numéro du template de VM à cloner
retries = 2
}
## La partie initialization "remplace" une partie des fichiers cloud-init
initialization {
datastore_id = "storage"
ip_config {
ipv4 {
address = "${each.value.ip}/24" # On attribue une IP à chaque VM
gateway = "192.168.1.1"
}
}
user_account {
password = each.value.password # On crée utilisateur et mot de passe.
username = each.value.username
keys = [
"ssh-rsa ..." # On ajoute sa clé ssh publique pour pouvoir ultérieurement se connecter sans mot de passe à chaque VM
]
}
}
keyboard_layout = "fr"
cpu {
type = "kvm64"
cores = var.cores
sockets = var.sockets
flags = []
}
memory {
dedicated = var.memory
}
network_device {
bridge = "vmbr0"
model = "virtio"
}
boot_order = ["scsi0"]
scsi_hardware = "virtio-scsi-single"
disk {
interface = "scsi0"
iothread = true
datastore_id = "<storage>"
discard = "ignore"
size = 10 # taille du disque de chaque VM
}
}
terraform init -upgrade ## initialiser et télécharger les providers et ressources mobilisés par la configuration
terraform validate # Tester la validité de la configuration
terraform plan # Obtenir l'aperçu de ce qui va être créé et déployé
Il est peut-être possible de réussir le déploiement en lançant simplement un terraform apply --auto-approve mais le déploiement d'un lot aussi important de VM a de grandes chances de créer des erreurs (dans mon cas, Proxmox ne parvenait pas à gérer autant de clonages, redimensionnements et démarrages simultanés). Par défaut, les clones sont lancés par lots de 10.
Pour éviter ce problème, il est possible de rajouter à la commande terraform apply --auto-approve le paramètre parallelism= en y adjoignant le nombre d'opérations simultanées à réaliser. Même en limitant ce paramètre à 2, le temps global de création restait raisonnable (12'30) et le déploiement s'effectue sans problème.
terraform apply --auto-approve -parallelism=2 # Pour lancer le déploiement et la création des VM (--auto-approve = créer sans prompt de confirmation)
En cas de problème :
terraform destroy # Après un déploiement, pour supprimer toutes les VM sur l'hôte Proxmox
dynamic "disk" {
for_each = local.instances
content {
interface = "scsi1" # veiller à numéroter en cohérence avec le ou les autre(s) disques
iothread = true
datastore_id = "<storage>"
size = 30
discard = "ignore"
file_format = "raw"
}
}
Fin de l'étape
Les 37 VM sont déployées et il est possible de se logger dessus depuis l'interface web de Proxmox avec le nom d'utilisateur affecté à la VM ou avec les utilisateurs ansible ou root.
En revanche, on ne peut se logger en ssh qu'avec l'utilisateur ansible.
L'étape suivante, avec Ansible, va donc avoir pour but de permettre d'ouvrir l'accès en ssh à chacun des utilisateurs (en modifiant la configuration de ssh), de déployer des programmes sur les VM ou encore de créer des répertoires, fichiers, etc.