Un homelab devient rapidement difficile à maintenir dès que le nombre de VM augmente, que plusieurs distributions coexistent ou que les tests doivent être répétés. Créer une machine dans l’interface Proxmox ou vCenter, modifier sa configuration à la main, puis installer les paquets via SSH fonctionne pour un essai isolé. Cette méthode ne tient plus lorsque l’objectif est de reconstruire un environnement complet après une erreur, une migration de stockage ou un changement de matériel.
L’approche Infrastructure as Code (IaC) sépare généralement deux responsabilités. Terraform décrit et crée les ressources d’infrastructure : VM, réseaux, disques, tags ou pools de ressources. Ansible configure ensuite les systèmes invités : comptes, paquets, services, fichiers, règles de pare-feu et applications. Terraform déclare l’existence de la machine ; Ansible déclare son état logiciel.
Cette séparation est particulièrement utile dans un homelab. Elle permet de détruire un cluster de test, de le redéployer avec les mêmes paramètres, puis d’appliquer une configuration identique sans dépendre d’une série de manipulations manuelles difficilement traçables.
Architecture IaC typique pour un homelab
Une architecture simple et efficace repose sur quatre composants :
- un hyperviseur, par exemple Proxmox VE ou vSphere ;
- Terraform, exécuté depuis un poste d’administration ou une VM dédiée ;
- Ansible, exécuté depuis le même poste ou depuis un conteneur d’automatisation ;
- un dépôt Git qui contient le code, les variables, les inventaires et la documentation.
Dans un premier temps, il est préférable de conserver une structure volontairement modeste :
homelab-iac/
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── terraform.tfvars.example
│ └── outputs.tf
├── ansible/
│ ├── inventory/
│ │ └── hosts.yml
│ ├── group_vars/
│ │ └── all.yml
│ ├── playbooks/
│ │ └── baseline.yml
│ └── roles/
│ └── common/
├── scripts/
│ └── generate-inventory.sh
├── .gitignore
└── README.md
Terraform conserve l’état des ressources créées dans un fichier terraform.tfstate. Pour un homelab personnel, un état local peut être acceptable, à condition de le sauvegarder. Dès que plusieurs personnes interviennent ou qu’une automatisation CI est ajoutée, il faut envisager un backend distant avec verrouillage, par exemple S3 compatible, GitLab HTTP backend ou Terraform Cloud.
Ansible n’a pas de fichier d’état comparable. Son fonctionnement est idempotent : un playbook doit pouvoir être relancé sans provoquer de modifications inutiles. L’état attendu est décrit dans les tâches, puis Ansible compare cette intention avec la machine cible.
Le point de jonction entre les deux outils est l’inventaire. Terraform connaît les adresses IP ou les noms DNS des VM qu’il crée. Ansible doit ensuite les utiliser comme cibles. Dans un environnement simple, un inventaire statique suffit. Dans un environnement plus automatisé, Terraform peut produire un fichier d’inventaire YAML ou JSON à partir de ses outputs. Pour aller plus loin, consultez notre guide de construction d’un homelab.
Préparer les images et les accès
L’automatisation ne compense pas une image de base incohérente. Avant de créer des VM avec Terraform, préparez un template propre pour chaque famille de système utilisée. Sous Proxmox, il s’agit souvent d’un template clonable depuis une image cloud Debian, Ubuntu, Rocky Linux ou AlmaLinux. Sous vSphere, ce sera une VM template ou une image gérée avec Content Library.
Pour les distributions Linux compatibles cloud-init, l’image doit idéalement inclure :
cloud-init;- un service SSH actif ;
- un utilisateur initial ou une configuration permettant l’injection de clé SSH ;
- le pilote du contrôleur disque et de la carte réseau virtuelle utilisés ;
- les outils invités adaptés, par exemple
qemu-guest-agentsous Proxmox ou VMware Tools sous vSphere.
Le guest agent est important. Il permet à l’hyperviseur de remonter l’adresse IP, l’état de la VM et parfois des informations complémentaires. Sans lui, Terraform peut créer correctement la machine tout en étant incapable d’attendre de manière fiable qu’elle soit joignable.
Les secrets ne doivent jamais être intégrés directement au dépôt Git. Les points sensibles habituels sont :
- jeton API Proxmox ;
- compte de service vCenter ;
- clé privée SSH ;
- mots de passe bootstrap ;
- jetons d’accès à un dépôt de paquets ou à un gestionnaire de secrets.
Une pratique minimale consiste à utiliser des variables d’environnement pour les identifiants Terraform et Ansible Vault pour les variables Ansible chiffrées. Pour un homelab plus mature, un gestionnaire tel que Vault, Bitwarden Secrets Manager ou une instance SOPS avec chiffrement par âge peut remplacer ces mécanismes.
Créer des VM déclaratives avec Terraform et Proxmox
Les providers Proxmox ne sont pas tous équivalents. Plusieurs implémentations communautaires existent, avec des modèles de ressources et des cycles de publication différents. Il faut donc lire la documentation du provider retenu, verrouiller sa version et tester les mises à jour hors production. L’exemple suivant illustre le principe avec le provider bpg/proxmox, utilisé avec des VM clonées depuis un template cloud-init.
Le fichier main.tf décrit ici trois noeuds de lab : un serveur d’automatisation, un noeud applicatif et un noeud de test.
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~> 0.66"
}
}
}
provider "proxmox" {
endpoint = var.proxmox_endpoint
api_token = var.proxmox_api_token
# A n'activer qu'en homelab si le certificat est auto-signé.
insecure = var.proxmox_insecure
}
resource "proxmox_virtual_environment_vm" "lab" {
for_each = var.vms
name = each.key
node_name = var.proxmox_node
vm_id = each.value.vm_id
clone {
vm_id = var.template_vm_id
full = true
}
cpu {
cores = each.value.cpu
type = "host"
}
memory {
dedicated = each.value.memory_mb
}
disk {
datastore_id = var.datastore_id
interface = "scsi0"
size = each.value.disk_gb
}
network_device {
bridge = var.network_bridge
}
initialization {
ip_config {
ipv4 {
address = each.value.ip_cidr
gateway = var.gateway_ipv4
}
}
user_account {
username = var.ssh_user
keys = [trimspace(file(var.ssh_public_key_path))]
}
dns {
servers = var.dns_servers
}
}
agent {
enabled = true
}
tags = ["homelab", "terraform", each.value.role]
}
La variable vms évite de dupliquer les blocs de ressources. Dans terraform.tfvars, elle peut être définie ainsi :
proxmox_endpoint = "https://pve.lab.example:8006/api2/json"
proxmox_node = "pve01"
template_vm_id = 9000
datastore_id = "local-lvm"
network_bridge = "vmbr0"
gateway_ipv4 = "192.168.50.1"
dns_servers = ["192.168.50.10", "1.1.1.1"]
ssh_user = "automation"
ssh_public_key_path = "~/.ssh/homelab_ed25519.pub"
vms = {
ansible01 = {
vm_id = 101
role = "automation"
cpu = 2
memory_mb = 4096
disk_gb = 32
ip_cidr = "192.168.50.21/24"
}
app01 = {
vm_id = 110
role = "application"
cpu = 2
memory_mb = 2048
disk_gb = 20
ip_cidr = "192.168.50.31/24"
}
test01 = {
vm_id = 111
role = "test"
cpu = 2
memory_mb = 2048
disk_gb = 20
ip_cidr = "192.168.50.32/24"
}
}
L’exécution suit le cycle Terraform habituel :
cd terraform
terraform init
terraform fmt -recursive
terraform validate
terraform plan -out=tfplan
terraform apply tfplan
Le fichier terraform.tfvars contient potentiellement des informations sensibles. Il ne doit pas être commité. Une bonne pratique consiste à versionner terraform.tfvars.example, sans jeton API ni données spécifiques au réseau domestique.
L’adresse IP statique dans cet exemple simplifie l’intégration avec Ansible. Une autre approche consiste à distribuer les baux via DHCP et à récupérer les adresses grâce au guest agent, aux enregistrements DNS ou à une source d’inventaire dynamique. Pour un homelab, les IP statiques déclarées sont souvent plus prévisibles et plus faciles à diagnostiquer.
Configurer les VM avec Ansible
Une fois les VM créées et démarrées, Ansible prend le relais. L’objectif du premier playbook n’est pas d’installer immédiatement une pile Kubernetes, un serveur Git ou une plateforme de supervision. Il faut d’abord appliquer un socle commun : mises à jour, outils de diagnostic, SSH, fuseau horaire, agent invité et règles de sécurité de base. Pour aller plus loin, consultez l’automatisation côté VMware avec PowerCLI.
Voici un inventaire statique minimal, inventory/hosts.yml :
all:
vars:
ansible_user: automation
ansible_ssh_private_key_file: ~/.ssh/homelab_ed25519
ansible_python_interpreter: /usr/bin/python3
children:
automation:
hosts:
ansible01:
ansible_host: 192.168.50.21
application:
hosts:
app01:
ansible_host: 192.168.50.31
test:
hosts:
test01:
ansible_host: 192.168.50.32
Le playbook suivant applique une configuration commune aux systèmes Debian et Ubuntu. Il illustre l’idempotence : les paquets sont déclarés présents, les services démarrés et activés, les fichiers rendus avec un contenu contrôlé.
---
- name: Appliquer le socle Linux du homelab
hosts: all
become: true
gather_facts: true
vars:
baseline_packages:
- qemu-guest-agent
- curl
- git
- vim
- htop
- ca-certificates
- unattended-upgrades
tasks:
- name: Mettre à jour le cache APT
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Installer les paquets de base
ansible.builtin.apt:
name: "{{ baseline_packages }}"
state: present
- name: Définir le fuseau horaire
community.general.timezone:
name: Europe/Paris
- name: Activer et démarrer QEMU Guest Agent
ansible.builtin.service:
name: qemu-guest-agent
state: started
enabled: true
- name: Désactiver l'authentification SSH par mot de passe
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
validate: '/usr/sbin/sshd -t -f %s'
notify: Redémarrer SSH
- name: Créer le répertoire de bannières
ansible.builtin.file:
path: /etc/issue.d
state: directory
mode: '0755'
- name: Ajouter une bannière de gestion
ansible.builtin.copy:
dest: /etc/issue.d/homelab.issue
content: |
Système géré par Ansible.
Les modifications manuelles doivent être documentées.
mode: '0644'
handlers:
- name: Redémarrer SSH
ansible.builtin.service:
name: ssh
state: restarted
Exécution :
cd ansible
ansible-playbook -i inventory/hosts.yml playbooks/baseline.yml
Avant de lancer un playbook pour la première fois, un contrôle de connectivité est utile :
ansible -i inventory/hosts.yml all -m ping
Dans un environnement réel, le playbook de base évoluera vers des rôles : common, docker, monitoring-agent, k3s-node, backup-client, etc. Les rôles évitent que les playbooks deviennent des fichiers monolithiques et favorisent la réutilisation entre plusieurs projets de lab.
Chaîner Terraform et Ansible sans couplage excessif
Il est tentant d’appeler Ansible directement depuis Terraform avec des provisioners local-exec ou remote-exec. Cette méthode fonctionne, mais elle crée un couplage fragile entre les deux outils. Terraform doit principalement gérer le cycle de vie des ressources ; Ansible doit gérer la configuration des systèmes.
Une chaîne simple et robuste est la suivante :
terraform applycrée ou met à jour les VM ;- Terraform expose les IP et rôles avec des outputs ;
- un script génère ou met à jour l’inventaire Ansible ;
ansible-playbookapplique la configuration ;- des tests de validation contrôlent le résultat.
Exemple d’output Terraform :
output "vm_addresses" {
value = {
for name, vm in proxmox_virtual_environment_vm.lab :
name => {
role = var.vms[name].role
ip = var.vms[name].ip_cidr
}
}
}
Pour une intégration plus propre, le script de génération peut consommer terraform output -json, supprimer le suffixe CIDR et construire un inventaire YAML. Si le lab utilise déjà un DNS interne fiable, il peut aussi produire des noms d’hôtes au lieu d’adresses IP.
Cette séparation permet de rejouer Ansible sans toucher à l’infrastructure. Elle permet aussi de modifier les ressources Terraform, par exemple en ajoutant de la mémoire à une VM, sans réinstaller toute la configuration applicative.
Reproductibilité, limites et discipline d’exploitation
Le premier bénéfice est la reproductibilité. Une VM supprimée par erreur peut être recréée avec le même nombre de vCPU, la même mémoire, le même réseau, les mêmes tags et la même configuration logicielle. Cela transforme le homelab en environnement jetable, ce qui est précisément l’un de ses intérêts pédagogiques. Pour aller plus loin, consultez le choix d’hyperviseur en homelab.
Le deuxième bénéfice est la traçabilité. Les changements passent par Git, donc il devient possible de répondre à des questions simples mais importantes : quand une VM a-t-elle été ajoutée, pourquoi le réseau a-t-il changé, quelle version d’un rôle Ansible a introduit un paquet, quelles valeurs étaient prévues pour le cluster de test ?
Le troisième bénéfice est la réduction de la dérive de configuration. Sans automatisation, deux VM censées être identiques finissent généralement par diverger : paquet installé à la main, fichier modifié lors d’un dépannage, mise à jour non appliquée, configuration SSH particulière. Ansible ne supprime pas toute dérive, mais il fournit un moyen concret de réimposer l’état attendu.
Il faut néanmoins connaître les limites :
- Terraform ne détecte pas toujours correctement les changements réalisés directement dans l’interface de l’hyperviseur, selon le provider ;
- certaines modifications de VM imposent un arrêt ou un remplacement de la ressource ;
- l’état Terraform est sensible : il peut contenir des identifiants, des adresses ou des métadonnées d’infrastructure ;
- cloud-init doit terminer avant qu’Ansible puisse accéder de manière stable à la machine ;
- les rôles Ansible doivent être testés sur une VM de validation avant application à l’ensemble du lab.
Une discipline utile consiste à réserver l’interface Proxmox ou vCenter au diagnostic et aux opérations d’urgence. Toute modification durable doit ensuite être reportée dans Terraform ou Ansible. Sinon, le code cesse progressivement de représenter l’état réel.
Enfin, documentez les dépendances externes : VLAN, réservation DHCP, DNS, stockage, certificats, dépôts APT et accès Internet. L’IaC rend l’environnement reproductible seulement si les prérequis autour de l’hyperviseur sont eux aussi connus et stables.
