Automatisation du déploiement d'une VM GNS3 sur Proxmox

Pendant mes études, j'ai eu besoin de déployer des VMs GNS3 sur mon cluster Proxmox. Pour rendre ce processus plus efficace, j'ai mis en place une solution complète d'automatisation qui utilise plusieurs outils DevOps modernes :
- Packer pour la création d'images
- Terraform pour le provisionnement d'infrastructure
- Ansible pour l'installation et la configuration des services
- Infisical pour la gestion des secrets
- GitLab CI/CD pour l'automatisation
Ce qui m'a particulièrement plu dans cette approche, c'est que tout est automatisé :
- L'image de base est créée automatiquement avec ma configuration (pas besoin de télécharger une ISO d'Ubuntu, de l'installer et de la configurer)
- Le déploiement se fait en un clic grâce à la pipeline CI/CD
- Les secrets sont gérés de manière sécurisée et centralisée
- La configuration réseau est flexible et adaptable selon les besoins
- Le tout est reproductible à l'identique
Rappel : GNS3 est un logiciel libre permettant l'émulation ou la simulation de réseaux informatiques.
La stack technique en détail
1. Packer : Création du template VM
Packer nous permet de créer une image VM standardisée qui servira de base pour notre serveur GNS3.
J'ai configuré ma template avec ces spécifications :
- CPU : 10 cœurs (nécessaire pour la virtualisation imbriquée)
- RAM : 48 GB (pour gérer plusieurs machines virtuelles)
- Disque : 400 GB (stockage des images et machines virtuelles)
- OS : Ubuntu 24.04 (Noble)
- Réseau : Configuration réseau précise
- VLAN : 10
- Adresse IP : 10.10.10.249/24 (permet de fonctionner avec le reverse proxy / FQDN déjà déployé)
L'objectif est simple : une fois la VM déployée, je peux y accéder soit :
- Depuis le client GNS3 via son FQDN (gns3.reverse9.xyz)
- Via la web-UI depuis l'adresse https://webui.gns3.reverse9.xyz/
Ces deux paramètres ont déjà été configurés sur le reverse proxy (Traefik) et le DNS (PowerDNS).
Configuration existante :
Dans PowerDNS, nous avons les enregistrements DNS suivants :gns3.reverse9.xyz -> A: 10.10.10.249/24webui.gns3.reverse9.xyz -> CNAME : traefik.reverse9.xyz -> A: 10.10.10.231
Dans Traefik, une configuration permet la redirection suivante :https://webui.gns3.reverse9.xyz -> http://gns3.reverse9.xyz:3080
Ce qui donne le schéma suivant :

Au lancement de la pipeline CI/CD, GitLab utilise le runner installé sur la VM "Provisionner" (où sont installés tous les outils) comme intermédiaire pour piloter les différents outils. Le processus se déroule comme suit :
- Le runner utilise Packer pour créer le template sur le cluster Proxmox
- Terraform clone ce template pour créer une nouvelle VM et la configure (réseau, utilisateur, clé SSH, etc.)
Ansible installe Docker et le serveur GNS3 à l'aide de deux playbooks dédiés

Note : Docker doit être installé au préalable : une erreur bloque l’installation de GNS3 via Ansible, bien que le script fonctionne manuellement
Configuration Packer détaillée
packer {
required_plugins {
proxmox = {
version = "~> 1"
source = "github.com/hashicorp/proxmox"
}
}
}
source "proxmox-iso" "ubuntu-server-noble" {
# Configuration Proxmox
node = "pve"
vm_id = "555"
vm_name = "gns3-template"
template_description = "GNS3VM Template from packer for Terraform"
# Configuration matérielle
cores = "10"
memory = "49152" # 48 GB
disks {
disk_size = "400G"
format = "raw"
storage_pool = "SSDK"
type = "virtio"
}
# Configuration réseau
network_adapters {
model = "virtio"
bridge = "vmbr0"
firewall = "false"
}
# Configuration Cloud-Init
cloud_init = true
cloud_init_storage_pool = "local"
}
Cette configuration définit :
- Les plugins requis pour Proxmox
- Les spécifications matérielles de la VM
- La configuration réseau
- L'activation de Cloud-Init pour la configuration post-déploiement
Configuration Cloud-Init pour Packer
#cloud-config
fqdn: gns3.reverse9.xyz
hostname: gns3vm
preserve_hostname: true
autoinstall:
version: 1
locale: en_US
keyboard:
layout: fr
ssh:
install-server: true
allow-pw: true
disable_root: true
ssh_quiet_keygen: true
allow_public_ssh_keys: true
packages:
- qemu-guest-agent
- sudo
storage:
layout:
name: direct
swap:
size: 0
user-data:
package_upgrade: false
timezone: Indian/Reunion
users:
- name: admin
groups: [adm, sudo]
lock-passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- # Votre clé publique SSH
Cette configuration Cloud-Init :
- Configure le hostname et le FQDN
- Met en place l'accès SSH sécurisé
- Installe les packages essentiels
- Crée un utilisateur admin avec les droits sudo

2. Terraform : Déploiement automatisé
Configuration Infisical Provider
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.42.0"
}
infisical = {
source = "infisical/infisical"
}
}
}
provider "infisical" {
host = "https://infisical.reverse9.xyz/"
client_id = var.infisical_client_id
client_secret = var.infisical_client_secret
}
Cette configuration :
- Définit les providers nécessaires (ici Proxmox)
- Configure l'accès à Infisical pour la gestion des secrets
Récupération des secrets
ephemeral "infisical_secret" "proxmox_endpoint" {
name = "PROXMOX_ENDPOINT"
env_slug = "prod"
workspace_id = "8732dcc5-9548-4466-b2db-e837aecc54de"
folder_path = "/Terraform"
}
ephemeral "infisical_secret" "proxmox_api_token" {
name = "PROXMOX_API_TOKEN"
env_slug = "prod"
workspace_id = "8732dcc5-9548-4466-b2db-e837aecc54de"
folder_path = "/Terraform"
}
Cette partie :
- Récupère les credentials Proxmox depuis Infisical
- Utilise des secrets éphémères pour plus de sécurité (pas de trace de credentials dans les tfstates)
Configuration de la VM
resource "proxmox_virtual_environment_vm" "vm" {
name = var.vm_hostname
node_name = var.target_node
cpu {
type = "host"
cores = 10
}
clone {
vm_id = var.source_vm_id
full = true
}
network_device {
model = "virtio"
bridge = var.proxmox_bridge
vlan_id = var.vm_vlan_id
}
initialization {
datastore_id = "SSDK"
user_data_file_id = proxmox_virtual_environment_file.cloud_user_config.id
ip_config {
ipv4 {
address = "${var.ip_address}/${var.netmask}"
gateway = var.gateway
}
}
}
}
Cette configuration :
- Clone le template créé par Packer
- Configure les ressources CPU
- Met en place le réseau avec VLAN
- Initialise la VM avec Cloud-Init
Cloud-Init pour Terraform
#cloud-config
hostname: gns3vm
fqdn: gns3.reverse9.xyz
users:
- name: ${username}
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ${ssh_key}
network:
version: 2
ethernets:
eth0:
nameservers:
addresses: [10.10.10.246]
search: [reverse9.xyz]
Cette configuration :
- Définit les paramètres réseau
- Configure les DNS
- Met en place l'utilisateur avec sa clé SSH
Les secrets sont gérés dans Infisical avec une organisation "Projets", et dans les projets, j'ai des dossiers par outil.
Projet : gns3-autosetup
├── Packer
│ ├── Tous les secrets de Packer
└── Terraform
└── Tous les secrets de Terraform
Les secrets sont structurés de la manière suivante dans l'application :



Note : On remarque la possibilité de définir différents secrets selon les environnements (Dev, Staging ou Pré-Prod, Prod)
3. Ansible : Installation et configuration
Installation de Docker
- name: Install docker
hosts: vlan10_infra
become: yes
tasks:
- name: Download docker install script
ansible.builtin.get_url:
url: https://get.docker.com
dest: /tmp/get-docker.sh
mode: '0755'
- name: Run docker install script
ansible.builtin.command: sh /tmp/get-docker.sh
Ce playbook :
- Télécharge le script d'installation Docker
- Exécute l'installation de manière automatisée
Installation de GNS3
- name: Install GNS3 with required components
hosts: vlan10_infra
become: yes
tasks:
- name: Download GNS3 remote install script
ansible.builtin.get_url:
url: https://raw.githubusercontent.com/GNS3/gns3-server/master/scripts/remote-install.sh
dest: /tmp/gns3-remote-install.sh
mode: '0755'
- name: Execute GNS3 installation script
ansible.builtin.command: bash /tmp/gns3-remote-install.sh --with-iou --with-i386-repository
Ce playbook :
- Installe GNS3 avec ses composants
- Active le support IOU
- Configure les dépôts nécessaires
4. GitLab CI/CD : Orchestration
stages:
- build
- deploy
- configure
variables:
GIT_DEPTH: "1"
build_template:
stage: build
script:
- echo "Building the VM template with Packer..."
- chmod +x Packer/packer-entrypoint.sh
- ./Packer/packer-entrypoint.sh
deploy_infrastructure:
stage: deploy
script:
- echo "Deploying infrastructure with Terraform..."
- chmod +x Terraform/terraform-entrypoint.sh
- ./Terraform/terraform-entrypoint.sh build
artifacts:
paths:
- Terraform/terraform.tfstate
expire_in: 1 hour
dependencies:
- build_template
configure_vm:
stage: configure
script:
- echo "Configuring VM with Ansible (Docker + GNS3)..."
- chmod +x Ansible/ansible-entrypoint.sh
- ./Ansible/ansible-entrypoint.sh
dependencies:
- deploy_infrastructure
Cette pipeline :
- Crée le template avec Packer
- Déploie l'infrastructure avec Terraform
- Configure la VM avec Ansible
- Conserve le tfstate comme artifact
On voit donc les étapes de la pipeline :

Le processus de déploiement en détail
1. Création du template (Packer)
Le processus commence avec Packer qui :
- Télécharge l'ISO Ubuntu 24.04
- Crée une VM sur Proxmox avec :
- Configuration matérielle optimisée pour GNS3
- Cloud-Init activé
- Installe le système avec :
- Les packages de base
- L'agent QEMU
- La configuration SSH
- Prépare le template pour Terraform

2. Déploiement de l'infrastructure (Terraform)
Terraform prend le relais pour :
- Récupérer les credentials depuis Infisical :
- Endpoint Proxmox
- Token API
- Configurations sensibles
- Créer une nouvelle VM avec :
- Clone du template
- Configuration réseau personnalisée
- Initialisation Cloud-Init
- Préparer la VM pour Ansible avec :
- Configuration DNS
- Accès SSH (user:publickey)

3. Configuration du serveur GNS3 (Ansible)
Finalement, Ansible :
- Installe Docker :
- Configuration du repository
- Installation des dépendances
- Configuration du daemon
- Installe GNS3 :
- Composants serveur
- Support IOU
- Dépendances système

Conclusion
Cette solution d'automatisation me permet de :
- Déployer un nouveau serveur GNS3 en quelques minutes
- Garantir une configuration cohérente et reproductible
- Gérer les secrets de manière sécurisée et centralisée
- Maintenir facilement l'infrastructure avec des outils modernes
Le code source complet est disponible sur GitHub

La topologie n'est pas complexe, mais on peut voir que GNS3 fonctionne parfaitement.
Pourquoi réinventer la roue ?
Après avoir fait tout cela, on peut se poser la question : pourquoi ne pas avoir simplement converti l'OVA GNS3 fourni par l'éditeur et importé le disque sur une VM dans Proxmox ?
Pour répondre à cette question, je vais peser le pour et le contre des deux approches.
Convertir la VM et l'importer
Avantages
- Gain de temps immense : pas besoin de se casser la tête à créer des playbooks Ansible, des configurations Terraform, un template Packer
- Simplicité : en 2 commandes, c'est terminé
Dans les grandes lignes, il suffit de :
- Télécharger la VM depuis le site de GNS3
- Décompresser l'archive obtenue
- Créer une VM sur Proxmox
- Convertir le VMDK en QCOW2
- Importer le disque sur une VM
Inconvénients
- Perte de flexibilité
- Impossible de personnaliser la configuration réseau (VLAN, IP, identifiants) avant le déploiement
- Apprentissage limité
- La conversion de formats de disque virtuel est une compétence basique
Stack CI/CD
Avantages CI/CD
- Personnalisation complète
- Reproductibilité garantie
- Gestion des secrets sécurisée
- Apprentissage des outils DevOps modernes :
- Packer
- Terraform
- Ansible
- GitLab CI/CD
Références
Quelques liens utilisés pour cet article


