Dieses Skript entfernt eine virtuelle Maschine (VM) in einem Hyper‑V Failover‑Cluster vollständig: Clusterressourcen, die VM selbst, ihre virtuellen Festplatten und den VM‑Ordner. Es enthält Sicherheitsprüfungen und robuste Pfadbehandlung, sodass keine Reste zurückbleiben.
Das Skript sucht die VM auf den angegebenen Hyper‑V‑Hosts, nimmt sie offline, entfernt die zugehörige Clustergruppe, löscht die VM und bereinigt die Dateien.
# ==========================================
# Hyper-V Cluster: VM komplett löschen
# ==========================================
# <Konfiguration> ---------------------------
$VMName = "VM02" # Name der zu löschenden VM (entspricht i. d. R. dem Clustergruppen-Namen)
$ClusterName = "CLUSTER01" # Clustername
# Liste der Hyper-V-Hosts (alternativ: automatisch via Get-ClusterNode)
$HyperVHosts = @("hyper-01","hyper-02","hyper-03")
$DryRun = $false # Sicherheitsmodus: $true = nur anzeigen, was passieren würde
# Alternativ statt fixer Liste:
# $HyperVHosts = (Get-ClusterNode -Cluster $ClusterName).Name
# <Hilfsfunktion> ---------------------------
function Remove-PathIfExists {
param(
[Parameter(Mandatory=$true)][string]$Path
)
if (Test-Path -LiteralPath $Path) {
if ($DryRun) {
Write-Host "DRY-RUN: Remove-Item '$Path' -Force" -ForegroundColor Yellow
} else {
Remove-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue
}
}
}
# <Host finden, auf dem die VM existiert> ---
$HyperVServer = $null
foreach ($HyperVHost in $HyperVHosts) {
try {
$vmProbe = Get-VM -Name $VMName -ComputerName $HyperVHost -ErrorAction Stop
if ($vmProbe) { $HyperVServer = $HyperVHost; break }
} catch {
continue
}
}
if (-not $HyperVServer) {
Write-Error "VM '$VMName' wurde auf keinem angegebenen Hyper-V-Host gefunden."
return
}
Write-Host "VM '$VMName' gefunden auf Host '$HyperVServer'." -ForegroundColor Green
# Vollständige VM-Infos (vor dem Entfernen sichern)
$VM = Get-VM -Name $VMName -ComputerName $HyperVServer
$vmPath = $VM.Path
# 1) VM stoppen (hart, falls noch läuft) -------------------------------
if ($VM.State -ne 'Off') {
Write-Host "VM ist nicht aus: Stoppe '$VMName' (hart) ..." -ForegroundColor Cyan
if ($DryRun) {
Write-Host "DRY-RUN: Stop-VM -Name '$VMName' -ComputerName '$HyperVServer' -Force -TurnOff"
} else {
Stop-VM -Name $VMName -ComputerName $HyperVServer -Force -TurnOff -ErrorAction SilentlyContinue
}
}
# 2) Clustergruppe ermitteln & entfernen ------------------------------
# Hinweis: Bei Cluster-VMs entspricht der Clustergruppen-Name meist dem VM-Namen.
$clusterGroup = Get-ClusterGroup -Cluster $ClusterName |
Where-Object { $_.GroupType -eq 'VirtualMachine' -and $_.Name -eq $VMName }
if ($clusterGroup) {
Write-Host "Clustergruppe '$($clusterGroup.Name)' gefunden. Setze offline und entferne Ressourcen ..." -ForegroundColor Cyan
if ($DryRun) {
Write-Host "DRY-RUN: Stop-ClusterGroup -Cluster '$ClusterName' -Name '$($clusterGroup.Name)'"
Write-Host "DRY-RUN: Remove-ClusterGroup -Cluster '$ClusterName' -Name '$($clusterGroup.Name)' -RemoveResources -Force -Confirm:\$false"
} else {
# Offline setzen (failsafe)
Stop-ClusterGroup -Cluster $ClusterName -Name $clusterGroup.Name -ErrorAction SilentlyContinue
# Entfernen inkl. Ressourcen
Remove-ClusterGroup -Cluster $ClusterName -Name $clusterGroup.Name -RemoveResources -Force -Confirm:$false
}
} else {
Write-Host "Keine passende Clustergruppe für '$VMName' gefunden (ggf. Standalone-VM oder bereits entfernt)." -ForegroundColor Yellow
}
# 3) Pfade der virtuellen Festplatten ermitteln ------------------------
# (Direkt sichern, bevor die VM entfernt wird)
$diskPaths = Get-VMHardDiskDrive -VMName $VMName -ComputerName $HyperVServer -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Path
if ($diskPaths) {
Write-Host "Virtuelle Festplatten (VHD/VHDX) werden entfernt:" -ForegroundColor Cyan
$diskPaths | ForEach-Object { Write-Host " - $_" }
} else {
Write-Host "Keine verbundenen VHD/VHDX ermittelt (ggf. bereits getrennt)." -ForegroundColor Yellow
}
# 4) VM aus Hyper-V entfernen -----------------------------------------
Write-Host "Entferne VM '$VMName' aus Hyper-V auf '$HyperVServer' ..." -ForegroundColor Cyan
if ($DryRun) {
Write-Host "DRY-RUN: Remove-VM -Name '$VMName' -ComputerName '$HyperVServer' -Force"
} else {
Remove-VM -Name $VMName -ComputerName $HyperVServer -Force -ErrorAction SilentlyContinue
}
# 5) Virtuelle Festplatten löschen ------------------------------------
foreach ($path in $diskPaths) {
Remove-PathIfExists -Path $path
}
# 6) VM-Ordnerinhalte löschen, dann Ordner & Elternordner bereinigen ---
if ($vmPath) {
Write-Host "Bereinige VM-Verzeichnis: '$vmPath' ..." -ForegroundColor Cyan
# Inhalte entfernen
if (Test-Path -LiteralPath $vmPath) {
if ($DryRun) {
Write-Host "DRY-RUN: Get-ChildItem -Path '$vmPath' | Remove-Item -Force"
} else {
Get-ChildItem -Path $vmPath -Force -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
}
# Ordner entfernen, wenn leer
$isEmpty = @(Get-ChildItem -Path $vmPath -Force -ErrorAction SilentlyContinue).Count -eq 0
if ($isEmpty) {
Remove-PathIfExists -Path $vmPath
}
# Elternordner bereinigen, wenn leer
$parentFolder = Split-Path -Path $vmPath -Parent
if ($parentFolder) {
$hasFiles = @(Get-ChildItem -Path $parentFolder -Force -Recurse -File -ErrorAction SilentlyContinue).Count -gt 0
if (-not $hasFiles) {
if ($DryRun) {
Write-Host "DRY-RUN: Remove-Item -Path '$parentFolder' -Force -Recurse"
} else {
Remove-Item -Path $parentFolder -Force -Recurse -ErrorAction SilentlyContinue
}
}
}
} else {
Write-Host "VM-Pfad existiert nicht (ggf. bereits bereinigt): '$vmPath'." -ForegroundColor Yellow
}
}
Write-Host "Fertig: VM '$VMName' wurde (inkl. Ressourcen & Dateien) entfernt." -ForegroundColor Green
Get-ClusterGroup mit GroupType=VirtualMachine finden und mit Remove-ClusterGroup -RemoveResources entfernen. Der im ursprünglichen Beispiel genutzte Parameter -VMId ist für Remove-ClusterGroup nicht üblich; stattdessen wird die Gruppen‑Bezeichnung verwendet.Get-VMHardDiskDrive liefert Objekte; deren Path wird explizit expandiert und erst danach via Remove-Item gelöscht. Direktes Piping der Objekte an Remove-Item ist nicht zuverlässig.$DryRun = $true siehst du vorab, was gelöscht würde. Erst auf $false stellen, wenn alles passt.Get-ClusterNode -Cluster <name> ermittelt werden (statt einer statischen Liste).C:\ClusterStorage\...; Pfade müssen auf dem Knoten existieren, der gerade arbeitet.H@ppy H@cking