Cluster Hyper-V avec S2D

Capture d'écran

Architecture du laboratoire

Ce laboratoire met en place un cluster Hyper-V avec Storage Spaces Direct (S2D) composé de plusieurs serveurs Windows Server 2025.

Les ressources et leurs caractéristiques sont les suivantes (à titre indicatif pour ce labo) :

Tous les serveurs (N1, N2, N3, DC, QUORUM) sont reliés à un commutateur virtuel interne nommé COM_LAN.
Le pare-feu IPFire est relié d'un côté à COM_EXT (vers l'extérieur) et de l'autre à COM_LAN (vers nos serveurs).

Seul le serveur DC est installé avec une interface graphique.
Tous les autres serveurs (N1, N2, N3, QUORUM) sont installés en mode Core.

Quorum peut être un serveur Windows ou Linux, du moment que vous êtes en mesure de créer un partage SMB dessus.
Dans les petits environnements Windows, on peut exceptionnellement placer le partage SMB pour le quorum directement sur le contrôleur de domaine (DC), même si ce n'est pas vraiment recommandé.
Cela économise une VM ici en cas de besoin !

Je vous fournis la totalité du code PowerShell nécessaire à la création du cluster de basculement Hyper-V sur S2D. Il s'agît de fonctions. Vous pouvez les copier/coller puis simplement appeler la fonction.


Plan de travail

Vous retrouverez ci-dessous, à chaque grande étape, l'explication et/ou les scripts PowerShell nécessaires.


1) Activer la virtualisation imbriquée pour N1, N2 et N3

Sur l'hôte Hyper-V physique (ou l'hôte lab) où sont créées les VM N1, N2 et N3 :

function fACTIVER.VIRTUALISATION.IMBRIQUEE {
clear
Write-Host "##############################"
Write-Host "# ACTIVER LA VIRTUALISATION IMBRIQUEE POUR UN ORDINATEUR VIRTUEL"
Write-Host " "
# Obtenez la liste des ordinateurs virtuels disponibles
$vms = Get-VM
# Affichez les ordinateurs virtuels disponibles avec un numéro
Write-Host "--------------------------------------"
Write-Host "LISTE DES ORDINATEURS VIRTUELS DISPONIBLES"
Write-Host " "
for ($i = 0; $i -lt $vms.Count; $i++) {
Write-Host "$($i+1): $($vms[$i].Name)"
}
Write-Host " "
$selectedVMNumber = Read-Host "ENTRER LE NUMERO DE L'ORDINATEUR VIRTUEL POUR ACTIVER LA VIRTUALISATION IMBRIQUEE"
$VMName = $vms[$selectedVMNumber - 1].Name
# Vérifiez si l'ordinateur virtuel est éteint
$vmStatus = (Get-VM -Name $VMName).State
if ($vmStatus -eq "Off") {
Set-VMProcessor -VMName $VMName -ExposeVirtualizationExtensions $true
Write-Host " "
Write-Host "VIRTUALISATION IMBRIQUEE ACTIVEE AVEC SUCCES."
} else {
Write-Host "Erreur : L'ORDINATEUR VIRTUEL '$VMName' DOIT ETRE ETEINT POUR ACTIVER LA VIRTUALISATION IMBRIQUEE."
}
}

Répétez cette opération pour N1, N2 et N3.


2) Renommer chaque serveur

Sur chaque serveur (N1, N2, N3, DC et QUORUM), vous pouvez soit :

function fCHANGER.NOM.ORDINATEUR {
########################################
# Changer le nom de l'ordinateur :
Clear-Host
Write-Host "########################"
Write-Host "# NOM DE L'ORDINATEUR"
Write-Host " "
$computerName = $env:COMPUTERNAME
Write-Host "NOM ACTUEL : $computerName"
Write-Host " "
$changeName = $null
while ($changeName -ne "O" -and $changeName -ne "N") {
$changeName = Read-Host "VOULEZ-VOUS LE CHANGER ? (O/N)"
Write-Host " "
}
if ($changeName -eq "O") {
$validName = $false
while (-not $validName) {
$newName = Read-Host "ENTRER LE NOUVEAU NOM"
if ($newName -match "^[a-zA-Z0-9-]{1,15}$") {
$validName = $true
Rename-Computer -NewName $newName -Force
Write-Host " "
$restart = $null
while ($restart -ne "O" -and $restart -ne "N") {
$restart = Read-Host "VOULEZ-VOUS REDEMARRER L'ORDINATEUR MAINTENANT ? (O/N)"
Write-Host " "
}
if ($restart -eq "O") {
Restart-Computer -Force
} else {
Write-Host "N'OUBLIEZ PAS DE REDEMARRER L'ORDINATEUR POUR PRENDRE EN COMPTE LE CHANGEMENT DE NOM."
}
} else {
Write-Host "NOM INVALIDE"
Write-Host " "
}
}
} else {
Write-Host "AUCUNE MODIFICATION APPORTEE."
}
}

Donnez-les noms exacts :

Redémarrez si nécessaire.


3) Configurer l'adressage IP

Toujours sur chaque serveur, en mode Core, vous pouvez utiliser sconfig, option Network Settings, ou utiliser la fonction :

function fCONFIGURATION.RESEAU {
Clear-Host
Write-Host "#########################"
Write-Host "# CONFIGURATION RESEAU"
Write-Host " "
$networkAdapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" }
$index = 0
$networkAdapters | ForEach-Object {
Write-Host "$index - $($_.Name)"
$index++
}
Write-Host " "
$selectedAdapterIndex = Read-Host "ENTRER LE NUMERO DE L'INTERFACE A CONFIGURER"
$selectedAdapter = $networkAdapters[$selectedAdapterIndex]
$newName = Read-Host "ENTRER LE NOUVEAU NOM DE L'INTERFACE (LAISSER VIDE POUR NE PAS CHANGER)"
if ($newName -ne "") {
Rename-NetAdapter -Name $selectedAdapter.Name -NewName $newName
$selectedAdapter = Get-NetAdapter | Where-Object { $_.Name -eq $newName }
}
$configurationChoice = Read-Host "TAPER 'DHCP' OU 'STATIQUE'"
if ($configurationChoice -eq "DHCP") {
Set-NetIPInterface -InterfaceAlias $selectedAdapter.Name -Dhcp Enabled
Clear
Write-Host "LA CARTE RESEAU A ETE CONFIGUREE VIA DHCP."
}
elseif ($configurationChoice -eq "STATIQUE") {
Write-Host
Write-Host "(IL VOUS FAUDRA ENTRER 2 FOIS VOTRE ADRESSE IP, C'EST NORMAL)"
Write-Host
$ipAddress = Read-Host "ENTRER L'IP (EX : 192.168.10.250)"
$subnetMask = Read-Host "ENTRER LE PREFIX (EX : 24)"
$gateway = Read-Host "ENTRER L'IP DE LA PASSERELLE (EX : 192.168.10.1)"
$dns1 = Read-Host "ENTRER L'IP DU DNS PRINCIPAL (LAISSER VIDE SI AUCUN)"
$dns2 = Read-Host "ENTRER L'IP DU DNS SECONDAIRE (LAISSER VIDE SI AUCUN)"
New-NetIPAddress -InterfaceAlias $selectedAdapter.Name -IPAddress $ipAddress -PrefixLength $subnetMask -DefaultGateway $gateway
if ($dns1 -ne "") {
Set-DnsClientServerAddress -InterfaceAlias $selectedAdapter.Name -ServerAddresses $dns1, $dns2
}
Clear
Write-Host "LA CARTE RESEAU A ETE CONFIGUREE."
}
}

Pour ce labo, vous avez indiqué :

Vérifiez la connectivité par ping sur votre réseau local.


4) Faire les mises à jour (Windows Update)

Pour automatiser, vous pouvez utiliser la fonction suivante :

function fMAJ {
if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) {
Write-Host "NuGet N'EST PAS INSTALLE. INSTALLATION EN COURS..."
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
}
if (-not (Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue)) {
Write-Host "LE REFERENTIEL PSGallery N'EST PAS CONFIGURE. CONFIGURATION EN COURS..."
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
}
if (-not (Get-Module -Name PSWindowsUpdate -ListAvailable -ErrorAction SilentlyContinue)) {
Write-Host "LE MODULE PSWindowsUpdate N'EST PAS ENCORE INSTALLE. INSTALLATION EN COURS..."
Install-Module -Name PSWindowsUpdate
}
Import-Module PSWindowsUpdate
# Passe en mode manuel pour les mises à jour
$RegPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
Set-ItemProperty -Path $RegPath -Name NoAutoUpdate -Type DWord -Value 1
Restart-Service -Name wuauserv -Force
Write-Host "LE MODE DE MISE-A-JOUR A ETE DEFINI SUR MANUEL"
# Installation des mises à jour trouvées
Install-WindowsUpdate -AcceptAll -AutoReboot
# Désactivation à la fin
Set-ItemProperty -Path $RegPath -Name NoAutoUpdate -Type DWord -Value 1
Set-ItemProperty -Path $RegPath -Name AUOptions -Type DWord -Value 1
Restart-Service -Name wuauserv -Force
Write-Host "LES MISES-A-JOUR ONT ETE DESACTIVEES"
Write-Host "RECHERCHE DES MISES-A-JOUR TERMINEE"
}

Exécutez fMAJ sur chaque serveur et redémarrez si nécessaire.


5) Installer le rôle Active Directory sur DC, promouvoir en contrôleur de domaine, créer la nouvelle forêt et le domaine TSSR.INFO

Sur le serveur DC (avec interface graphique ou en PowerShell) :

function fADDS {
$nomForet = Read-Host "VEUILLEZ ENTRER LE NOM DE LA FORET A CREER POUR LE CONTROLEUR DE DOMAINE"
$dsrmPassword = Read-Host "ENTRER LE MOT DE PASSE POUR LE MODE DE RECUPERATION DES SERVICES D'ANNUAIRE (DSRM)" -AsSecureString
Write-Host " "
Install-WindowsFeature -Name AD-Domain-Services,RSAT-AD-AdminCenter,RSAT-ADDS-Tools,RSAT-AD-PowerShell -IncludeManagementTools
Write-Host " "
Install-ADDSForest -DomainName $nomForet -InstallDNS -SafeModeAdministratorPassword $dsrmPassword -Force
}

Par exemple, vous indiquez TSSR.INFO comme nom de forêt et domaine. Le serveur DC va redémarrer à la fin de la promotion.


6) Faire rejoindre le domaine TSSR.INFO à N1, N2, N3 et QUORUM

Sur chaque serveur Core (N1, N2, N3, QUORUM) :

OU utiliser la fonction PowerShell :

function fREJOINDRE.DOMAINE {
clear
Write-Host "#########################"
Write-Host "# REJOINDRE UN DOMAINE"
Write-Host " "
$networkAdapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" }
$index = 0
$networkAdapters | ForEach-Object {
Write-Host "$index - $($_.Name)"
$index++
}
Write-Host " "
$selectedAdapterIndex = Read-Host "ENTRER LE NUMERO DE L'INTERFACE A UTILISER"
$selectedAdapter = $networkAdapters[$selectedAdapterIndex]
$configureDNS = Read-Host "VOULEZ-VOUS CONFIGURER LE DNS ? (O/N)"
if ($configureDNS -eq "O") {
$dns1 = Read-Host "ENTRER L'IP DU DNS PRINCIPAL"
$dns2 = Read-Host "ENTRER L'IP DU DNS SECONDAIRE (LAISSER VIDE SI AUCUN)"
Set-DnsClientServerAddress -InterfaceAlias $selectedAdapter.Name -ServerAddresses $dns1, $dns2
}
$domainName = Read-Host "ENTRER LE NOM DU DOMAINE"
$username = Read-Host "ENTRER LE NOM D'UTILISATEUR AVEC LES DROITS DE JOINDRE AU DOMAINE"
$password = Read-Host -AsSecureString "ENTRER LE MOT DE PASSE DE L'UTILISATEUR"
try {
Add-Computer -DomainName $domainName -Credential (New-Object PSCredential $username, $password) -Restart -Force
clear
Write-Host "L'ORDINATEUR A ETE REDEMARRE ET A REJOINT LE DOMAINE AVEC SUCCES."
}
catch {
clear
Write-Host "UNE ERREUR S'EST PRODUITE LORS DE LA JOINTURE AU DOMAINE :"
Write-Host $_.Exception.Message
}
}

Après redémarrage, vérifiez que vos serveurs sont bien dans le domaine.


7) Installer le rôle Hyper-V sur N1, N2, N3 et DC

Même si votre DC n'hébergera sans doute pas de machines virtuelles en production, on vous demande de l'installer pour ce labo :

Sur chaque serveur (Core ou GUI) :

function fINSTALLER.ROLE.HYPERV {
Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart
}

8) Installer le rôle Serveur de fichiers sur tous les serveurs

Ce rôle est requis pour S2D (entre autres, car Storage Spaces Direct repose sur la couche fichiers, CSV, etc.) :

function fINSTALLER.SERVEUR.FICHIER {
Install-WindowsFeature -Name FS-FileServer -IncludeManagementTools
}

Faites-le sur N1, N2, N3, DC, QUORUM.


9) Installer la fonctionnalité de cluster de basculement sur N1, N2, N3 et DC

Le cluster pourra être géré aussi depuis DC (via le Gestionnaire de cluster) :

function fINSTALLER.FONCTIONNALITE.CLUSTER.BASCULEMENT {
Install-WindowsFeature -Name Failover-Clustering -IncludeManagementTools
}

Faites-le sur N1, N2, N3 et DC.


10) Créer le cluster « HCIcluster » à partir de N1

Vous pouvez créer le cluster via l'interface graphique (Failover Cluster Manager) ou en PowerShell. Voici une fonction exemplaire :

function fCREER.CLUSTER {
Clear-Host
Write-Host "| ========= -------------------------------------- ========= |" -ForegroundColor Cyan
Write-Host "| ========= CREATION D'UN CLUSTER HYPER-V ========= |" -ForegroundColor Cyan
Write-Host
Write-Host "| CONFIRMEZ-VOUS LES PRE-REQUIS SUIVANTS ?"
Write-Host "| - Tous les serveurs sont identiques et dans le même domaine"
Write-Host "| - Les rôles / fonctionnalités Hyper-V, File Server, et Clustering sont installés"
Write-Host "| - Les mises à jour sont faites"
Write-Host "| - Vous lancez le script uniquement sur un des noeuds (ex: N1)"
Write-Host " "
$confirm = Read-Host "| SI TOUT EST OK, VOUS POUVEZ CONTINUER (O/N)"
if ($confirm -ne "O") { return }
$nodes = Read-Host "Veuillez entrer les noms des serveurs à mettre en cluster, séparés par une virgule"
$nodesArray = $nodes.Split(',')
# Vérification de l'accessibilité
foreach ($node in $nodesArray) {
if (Test-Connection -ComputerName $node -Count 1 -Quiet) {
Write-Output "$node est accessible."
} else {
Write-Error "$node n'est pas accessible. Vérifiez la connectivité."
return
}
}
# Installer RSAT-Clustering si besoin
try {
Install-WindowsFeature -Name RSAT-Clustering -IncludeAllSubFeature -Verbose
} catch {
Write-Error "Erreur lors de l'installation RSAT-Clustering : $_"
return
}
# Test-Cluster
try {
Test-Cluster -Node $nodesArray
Write-Output "Le test du cluster a été lancé avec succès."
$ClusterIPaddress = Read-Host "Veuillez entrer l'Adresse IP du futur cluster"
$ClusterName = Read-Host "Veuillez entrer un nom pour le futur cluster"
$ClusterIPaddressCollection = New-Object System.Collections.Specialized.StringCollection
$ClusterIPaddressCollection.Add($ClusterIPaddress)
# Create cluster
try {
New-Cluster -Name $ClusterName -Node $nodesArray -StaticAddress $ClusterIPaddressCollection -Verbose
Write-Output "Le cluster '$ClusterName' a été créé avec succès."
Start-Sleep -Seconds 10
Get-ClusterNode -Cluster "$ClusterName"
$clusterIP = (Get-ClusterResource -Cluster $clusterName | Where-Object { $_.ResourceType -eq "IP Address" } | Get-ClusterParameter -Name "Address").Value
Write-Output "L'adresse IP du cluster $clusterName est : $clusterIP"
} catch {
Write-Error "Erreur lors de la création du cluster : $_"
}
} catch {
Write-Error "Erreur lors du test du cluster : $_"
}
}

Par exemple, vous pouvez nommer le cluster : HCIcluster, et lui attribuer une IP libre (ex: 10.10.10.40).


11) Créer le commutateur externe COM_EXT sur N1, N2 et N3

Sur chaque hôte Hyper-V (N1, N2, N3), ouvrez une session PowerShell et exécutez :

New-VMSwitch -Name "COM_EXT" -NetAdapterName "<nom_de_votre_interface>" -AllowManagementOS $true

Dans un labo virtuel (virtualisation imbriquée), vous pouvez devoir sélectionner la carte réseau (ou l'interface) qui sort vers votre IPFire (COM_EXT).
Vérifiez dans Get-NetAdapter quel est le nom de l'interface à utiliser.


12) Ajouter 2 disques .vhdx dynamiques de 127 Go à N1, N2 et N3

Dans votre hôte physique ou sur votre application d'hypervision (Hyper-V Manager) :


13) Préparer les disques pour S2D

Sur chaque noeud (N1, N2, N3), exécutez la fonction suivante après avoir vérifié que vos disques additionnels sont bien détectés (via Get-Disk ou Get-PhysicalDisk) :

function fPREPARER.DISQUES.S2D {
Clear-Host
Write-Host "| ========= -------------------------------------- ========= |" -ForegroundColor Cyan
Write-Host "| ========= PREPARER LES DISQUES POUR S2D ========= |" -ForegroundColor Cyan
Write-Host
Write-Host "| - Au moins un disque pour S2D sur chaque noeud"
Write-Host "| - Même nombre de disques identiques sur chaque noeud"
Write-Host "| - Cluster déjà opérationnel"
Write-Host " "
$confirm = Read-Host "| SI TOUT EST OK, VOUS POUVEZ CONTINUER (O/N)"
if ($confirm -ne "O") { return }
$computerName = $env:COMPUTERNAME
Invoke-Command ($computerName) {
Update-StorageProviderCache
Get-StoragePool | ? IsPrimordial -eq $false | Set-StoragePool -IsReadOnly:$false -ErrorAction SilentlyContinue
Get-StoragePool | ? IsPrimordial -eq $false | Get-VirtualDisk | Remove-VirtualDisk -Confirm:$false -ErrorAction SilentlyContinue
Get-StoragePool | ? IsPrimordial -eq $false | Remove-StoragePool -Confirm:$false -ErrorAction SilentlyContinue
Get-PhysicalDisk | Reset-PhysicalDisk -ErrorAction SilentlyContinue
Get-Disk | ? Number -ne $null | ? IsBoot -ne $true | ? IsSystem -ne $true | ? PartitionStyle -ne RAW | % {
$_ | Set-Disk -isoffline:$false
$_ | Set-Disk -isreadonly:$false
$_ | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false
$_ | Set-Disk -isreadonly:$true
$_ | Set-Disk -isoffline:$true
}
Get-Disk | Where Number -Ne $Null | Where IsBoot -Ne $True | Where IsSystem -Ne $True | Where PartitionStyle -Eq RAW | Group -NoElement -Property FriendlyName
} | Sort -Property PsComputerName, Count
}

Répétez sur N1, N2 et N3.


14) Activer S2D

Cette commande s'exécute une seule fois (typiquement sur le noeud 1, N1). Elle va scanner le cluster, trouver les disques en RAW non attribués et construire un pool S2D.

function fACTIVER.S2D {
Clear-Host
Write-Host "| ========= ACTIVER S2D ========= |"
$confirm = Read-Host "| SI TOUT EST OK, VOUS POUVEZ CONTINUER (O/N)"
if ($confirm -ne "O") { return }
Enable-ClusterS2D
Start-Sleep 10
$poolName = (Get-StoragePool | Where-Object FriendlyName -Like "S2D on *").FriendlyName
Write-Host "| LE POOL S2D CREE SE NOMME : $poolName"
Pause
}

15) Créer le disque virtuel (vDisk) miroir triple S2D

Sur N1 (ou le noeud où vous administrez) :

function fCREER.VDISK.S2D {
Clear-Host
Write-Host "| ========= CREER UN DISQUE VIRTUEL POUR S2D ========= |"
$confirm = Read-Host "| SI TOUT EST OK, VOUS POUVEZ CONTINUER (O/N)"
if ($confirm -ne "O") { return }
$ndisk = Read-Host "| COMBIEN DE DISQUES DEVOLUS A S2D PAR NOEUD AVEZ-VOUS ?"
switch ($ndisk) {
"1" {
$poolName = (Get-StoragePool | Where-Object FriendlyName -Like "S2D on *").FriendlyName
Write-Host "| - CREATION D'UN VDISK MIROIR A RECOPIE DOUBLE"
New-VirtualDisk -StoragePoolFriendlyName $poolName `
-FriendlyName "S2D_VDISK" `
-ResiliencySettingName Mirror `
-NumberOfDataCopies 2 `
-UseMaximumSize `
-ProvisioningType Fixed
}
"2" {
$poolName = (Get-StoragePool | Where-Object FriendlyName -Like "S2D on *").FriendlyName
Write-Host "| - CREATION D'UN VDISK MIROIR A RECOPIE TRIPLE"
New-VirtualDisk -StoragePoolFriendlyName $poolName `
-FriendlyName "S2D_VDISK" `
-ResiliencySettingName Mirror `
-NumberOfDataCopies 3 `
-UseMaximumSize `
-ProvisioningType Fixed
}
default {
Write-Host "| CHOIX INVALIDE. VEUILLEZ REESSAYER." -ForegroundColor Yellow
Pause
}
}
Pause
}

Dans votre cas, vous avez 2 disques par noeud. Cela permettra du triple miroir (3 copies répliquées entre 3 noeuds).


16) Créer un volume sur le vDisk S2D et le formater

Toujours depuis N1 :

function fCREER.VOLUME.VDISK.S2D {
$labelLecteur = "DATA_S2D"
$VirtualDisk = Get-VirtualDisk -FriendlyName "S2D_VDISK" -ErrorAction Stop
$disk = $VirtualDisk | Get-Disk
$ClusterDisk = Get-ClusterResource | Where-Object { $_.ResourceType -eq "Physical Disk" -and $_.Name -like "*S2D_VDISK*" }
if ($ClusterDisk) {
Suspend-ClusterResource -Name $ClusterDisk.Name
$partition = $disk | New-Partition -AssignDriveLetter -UseMaximumSize
$partition | Format-Volume -FileSystem ReFS -NewFileSystemLabel "$labelLecteur" -Confirm:$false
Resume-ClusterResource -Name $ClusterDisk.Name
} else {
Write-Error "Le disque de cluster n'a pas été trouvé."
}
Pause
}

Cela va créer un volume ReFS sur votre nouveau disque virtuel Storage Spaces Direct.


17) Gérer le cluster et valider le stockage S2D depuis DC

Votre volume S2D apparaît alors sur chacun de vos noeuds dans C:\ClusterStorage\S2D_VDISK
Vous devrez donc stocker vos VM dans cet emplacement.


18) Configurer un partage SMB sur QUORUM, l'utiliser comme Quorum

(Si vous choisissez de placer le partage SMB pour le quorum sur le contrôleur de domaine (ou sur un autre serveur Windows), faîtes attention aux autorisations (partage et droits NTFS). Si vous partagez directement un lecteur, vous devez uniquement ajouter le cluster "HCIcluster" (après avoir bien sélectionné le type d'objet 'Ordinateurs'). Si vous créez un partage sur un dossier à l'intérieur d'un volume, vous devrez également ajouter le groupe des Administrateurs.)

New-SmbShare -Name "QuorumShare" -Path "D:\QuorumShare" -FullAccess "TSSR\Administrators"

Le cluster va ainsi stocker les informations de quorum sur un partage SMB hébergé sur votre serveur QUORUM.


Conclusion

Vous disposez maintenant :

Vous pouvez désormais créer des machines virtuelles Hyper-V (en CSV) sur ce cluster et profiter de la résilience apportée par S2D et le Failover Clustering.

Bonne mise en pratique !


⬆️ Retour en haut de la page