Kurohs Blog

Ein Sammelsurium des Wissens

Hyper-V Backup Skript: Me vs ChatGPT

Serverraum mit einem Roboter, der ein rotes Cape trägt

Einleitung

Vor einiger Zeit habe ich mir für zu Hause einen Beelink Mini PC zugelegt. Aktuell läuft darauf nur eine einzige virtuelle Maschine – der Home Assistant in seiner Grundinstallation. Mein Plan ist es, nach und nach weitere VMs hinzuzufügen. Besonders ins Auge gefasst habe ich dabei Paperless-ngx, um meine Dokumente digital zu archivieren und zu verwalten.

Während ich mir Gedanken über Speicherplatz und Erweiterungsmöglichkeiten mache, kommt mir eine viel grundlegendere Frage in den Sinn: Wie sichere ich eigentlich meine VMs? Denn was bringt es mir, neue VMs aufzusetzen, wenn ich im Falle eines Fehlers oder Datenverlusts keine Backups habe? Genau – gar nichts! Ohne Sicherung wäre die ganze Arbeit umsonst. Deshalb steht das Thema Backup jetzt an erster Stelle.

Idealerweise würde ich dafür ein Synology NAS nutzen, das sich hervorragend für automatisierte Backups eignet. Allerdings ist mir die Anschaffung derzeit zu teuer – andere Dinge haben Vorrang. Ein dedizierter Backup-Server wäre eine Alternative, aber auch dafür bräuchte ich neue Hardware, denn die Sicherung sollte nicht auf demselben Gerät laufen wie die VMs. Also scheidet auch das aus.

Eine Möglichkeit bleibt: Hyper-V bietet die Option, VMs per Export zu sichern. Der Haken? Ich müsste den Export regelmäßig manuell anstoßen – und darauf habe ich keine Lust. Also entscheide ich mich, das Ganze mit einem PowerShell-Skript zu automatisieren.

Im Folgenden stelle ich dir mein Skript und die von ChatGPT erstellte Version vor – mit einer Analyse der Unterschiede und meiner Einschätzung dazu.

Mein gebasteltes Hyper-V Backup Skript

Was macht das Skript?

Das Skript führt die folgenden Aufgaben aus:

  1. Exportiert alle Hyper-V-VMs in einen definierten Backup-Ordner.
  2. Löscht ältere Backups, um Speicherplatz zu sparen.
  3. Sendet eine Benachrichtigung an die App ntfy bei erfolgreichem Export oder im Fehlerfall.

Voraussetzungen

  • Windows mit installiertem PowerShell (ab Version 5.0 empfohlen).
  • Hyper-V muss auf dem System aktiv sein.
  • Die App ntfy für Benachrichtigungen.

Mein Code

clear
Write-Host " "
Write-Host "#################################################################################################################################" -ForegroundColor Green
Write-Host "#                                                                                                                               #" -ForegroundColor Green
Write-Host "#       VM_Export.ps1                                                                                                           #" -ForegroundColor Green
Write-Host "#       Written by Daniel Jörges aka Kuroh - www.kurohs-blog.de                                                                 #" -ForegroundColor Green
Write-Host "#                                                                                                                               #" -ForegroundColor Green
Write-Host "#       Beschreibung: Exportiert alle VMs auf dem Hyper-V Host unter angebebenem Pfad und sendet eine ntfy Nachricht            #" -ForegroundColor Green
Write-Host "#                     an das angegebene Topic. Es wird eine bestimmte Anzahl Backups aufbewahrt. Alles darüber wird gelöscht.   #" -ForegroundColor Green
Write-Host "#                                                                                                                               #" -ForegroundColor Green
Write-Host "#################################################################################################################################" -ForegroundColor Green
Write-Host " "

## Variablen initialisieren ##
[String]$dateToday = ""
[String]$workingDir = ""
[String]$backupDir = ""
[Int]$keepBackups = ""
[String]$ntfyTopic = ""

## Variablen befüllen ##
$dateToday = get-date -format yyyy-MM-dd
$workingDir = "C:\VM-Backup\" #Das Verzeichnis in dem die Backups gespeichert werden.
$backupDir = $workingDir + $dateToday
$keepBackups = 3 # Gibt an wie viele Backups behalten werden. Alles darüber wird gelöscht!
$ntfyTopic = "VMBackup" #Hier kommt das zu abonierende Topic für die App ntfy rein. Somit erhält man dann die Benachrchitigungen aufs Handy
$logFile = "$workingDir\log.txt"


## POST Request für ntfy initialisieren ##
$Request = @{}

## Funktion, die sowohl in die Konsole als auch in die Logdatei schreibt
function Write-Log {
    param (
        [string]$Message,
        [string]$LogFilePath
    )

    # Schreibe die Nachricht in die Konsole
    Write-Output $Message
    # Schreibe die Nachricht in die Logdatei
    $Message | Out-File -FilePath $LogFilePath -Encoding utf8 -Append
}

## Funktion zum Exportieren aller VMs mit nfty Nachricht an mein Handy ##
function Export-MyVMs {
    if(-not(Test-Path $backupDir)){
        Write-Log -Message "VMs werden exportiert..." -LogFilePath $logFile
        Get-VM | Export-VM -Path $backupDir
        if (Test-Path $backupDir){
            Write-Log -Message "Export beendet." -LogFilePath $logFile
            $Request = @{
              Method = "POST"
              URI = "https://ntfy.sh/$ntfyTopic"
              Body = "VM-Export erfolgreich."
            }
            Invoke-RestMethod @Request | Out-File -FilePath $logFile -Encoding utf8 -Append
        }
    }
}

## Löscht die ältesten Backups ##
function Remove-MyOldVMExports{
    Write-Log -Message "Lösche alte Backups..." -LogFilePath $logFile
    Get-ChildItem $workingDir -Directory |
        Sort-Object FullName -Desc |
        Select-Object -Skip $keepBackups |
        ForEach-Object {
            Remove-Item -Path $_.FullName -Recurse -Force -Verbose
        } 4>&1 | Out-File -FilePath $logFile -Encoding utf8 -Append
    Write-Log -Message "Alte Backups wurden bereinigt." -LogFilePath $logFile
}

## Abfrage, ob der Pfad zum Backup-Verzeichnis vorhanden ist, und Funktionsaufrufe ##
if (Test-Path $workingDir){
    Export-MyVMs
    Remove-MyOldVMExports
}else{
    $Request = @{
      Method = "POST"
      URI = "https://ntfy.sh/$ntfyTopic"
      Body = "Pfad für Export nicht gefunden!"
    }
    Invoke-RestMethod @Request | Out-File -FilePath $logFile -Encoding utf8 -Append
}

Erklärung des Codes

Hier die Funktionen und der Ablauf des Skripts im Detail:

1. Initialisierung und Konfiguration

Zu Beginn definiert das Skript einige Variablen, um die Arbeitsumgebung festzulegen.

$dateToday = get-date -format yyyy-MM-dd
$workingDir = "C:\VM-Backup\" # Ordner für die Backups
$backupDir = $workingDir + $dateToday
$keepBackups = 3 # Anzahl der zu behaltenden Backups
$ntfyTopic = "VMBackup" # Topic für Benachrichtigungen in ntfy
$logFile = "$workingDir\log.txt" # Logdatei
  • $dateToday: Erstellt einen Unterordner basierend auf dem aktuellen Datum.
  • $workingDir: Hauptordner, in dem alle Backups gespeichert werden.
  • $keepBackups: Definiert, wie viele Backups aufbewahrt werden sollen. Ältere Backups werden gelöscht.
  • $ntfyTopic: Das Benachrichtigungsthema, das in der App verwendet wird.
  • $logFile: Eine Logdatei, um alle Aktionen zu protokollieren.
2. Logging-Funktion

Damit das Skript sowohl in der Konsole als auch in einer Logdatei nachvollziehbar bleibt, wird eine Logging-Funktion definiert:

function Write-Log {
    param (
        [string]$Message,
        [string]$LogFilePath
    )
    Write-Output $Message
    $Message | Out-File -FilePath $LogFilePath -Encoding utf8 -Append
}
  • Eingabeparameter: Die Funktion benötigt die zu protokollierende Nachricht und den Pfad zur Logdatei.
  • Doppelte Ausgabe: Die Nachricht wird sowohl in der Konsole als auch in der Logdatei gespeichert.
3. Export der VMs

Die Hauptfunktion des Skripts exportiert alle VMs in den angegebenen Backup-Ordner.

function Export-MyVMs {
    if(-not(Test-Path $backupDir)){
        Write-Log -Message "VMs werden exportiert..." -LogFilePath $logFile
        Get-VM | Export-VM -Path $backupDir
        if (Test-Path $backupDir){
            Write-Log -Message "Export beendet." -LogFilePath $logFile
            $Request = @{
              Method = "POST"
              URI = "https://ntfy.sh/$ntfyTopic"
              Body = "VM-Export erfolgreich."
            }
            Invoke-RestMethod @Request | Out-File -FilePath $logFile -Encoding utf8 -Append
        }
    }
}
  • Get-VM | Export-VM: Alle VMs auf dem Host werden exportiert.
  • Benachrichtigung: Nach erfolgreichem Export sendet das Skript eine Erfolgsmeldung an ntfy.
4. Bereinigung alter Backups

Um Speicherplatz zu sparen, löscht das Skript automatisch die ältesten Backups, sobald die festgelegte Anzahl überschritten wird.

function Remove-MyOldVMExports {
    Write-Log -Message "Lösche alte Backups..." -LogFilePath $logFile
    Get-ChildItem $workingDir -Directory |
        Sort-Object FullName -Desc |
        Select-Object -Skip $keepBackups |
        ForEach-Object {
            Remove-Item -Path $_.FullName -Recurse -Force -Verbose
        } 4>&1 | Out-File -FilePath $logFile -Encoding utf8 -Append
    Write-Log -Message "Alte Backups wurden bereinigt." -LogFilePath $logFile
}
  • Sort-Object und Skip: Ordnet die Backup-Ordner nach Namen und überspringt die neuesten (aktuell festgelegt auf 3).
  • Remove-Item: Löscht die ältesten Ordner und gibt den Speicherplatz frei.
5. Hauptablauf des Skripts

Zum Schluss wird überprüft, ob der Hauptordner existiert, und die oben definierten Funktionen werden aufgerufen:

if (Test-Path $workingDir){
    Export-MyVMs
    Remove-MyOldVMExports
} else {
    $Request = @{
      Method = "POST"
      URI = "https://ntfy.sh/$ntfyTopic"
      Body = "Pfad für Export nicht gefunden!"
    }
    Invoke-RestMethod @Request | Out-File -FilePath $logFile -Encoding utf8 -Append
}
  • Test-Path: Überprüft, ob der Hauptordner für Backups existiert.
  • Fehlerbenachrichtigung: Falls der Pfad nicht gefunden wird, sendet das Skript eine Fehlermeldung an ntfy.

Das Hyper-V Backup Skript von ChatGPT

Was macht das Skript?

Das Skript führt die folgenden Aufgaben aus:

  1. Exportiert alle Hyper-V-VMs in einen definierten Backup-Ordner.
  2. Löscht ältere Backups, um Speicherplatz zu sparen.
  3. Erstellt eine Log-Datei mit Zeitstempel
  4. Fehlerhandhabung mit Protokollierung

Voraussetzungen

  • Windows mit installiertem PowerShell (ab Version 5.0 empfohlen).
  • Hyper-V muss auf dem System aktiv sein.

Der Code von ChatGPT

# ====================================================================================
# Skript: Export-HyperVVMs.ps1
# Beschreibung: Automatisches Exportieren von Hyper-V-VMs für Backups
# Autor: [Dein Name]
# ====================================================================================

# ======= Konfiguration =======
# Verzeichnis für den Export
$exportDir = "C:\HyperVExports"

# Maximale Anzahl von Backups, die beibehalten werden sollen
$keepBackups = 3

# Protokolldatei
$logFile = "C:\HyperVExports\ExportLog.txt"

# ======= Funktionen ========

# Funktion: Log-Schreiben (Konsole + Datei)
function Write-Log {
    param (
        [string]$Message,
        [string]$LogFilePath
    )

    # Zeitstempel hinzufügen
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] $Message"

    # Ausgabe in Konsole und Logdatei
    Write-Output $logMessage
    $logMessage | Out-File -FilePath $LogFilePath -Encoding utf8 -Append
}

# Funktion: Alte Backups bereinigen
function Cleanup-OldBackups {
    param (
        [string]$BackupDir,
        [int]$MaxBackups
    )

    Write-Log -Message "Starte Bereinigung alter Backups im Verzeichnis '$BackupDir'..." -LogFilePath $logFile

    # Alle vorhandenen Backups sortieren (älteste zuerst)
    $backups = Get-ChildItem -Path $BackupDir -Directory | Sort-Object CreationTime

    # Wenn mehr als die maximale Anzahl vorhanden ist, alte löschen
    if ($backups.Count -gt $MaxBackups) {
        $toDelete = $backups | Select-Object -First ($backups.Count - $MaxBackups)

        foreach ($backup in $toDelete) {
            Write-Log -Message "Lösche altes Backup: $($backup.FullName)" -LogFilePath $logFile
            Remove-Item -Path $backup.FullName -Recurse -Force -ErrorAction Stop
        }
    } else {
        Write-Log -Message "Keine alten Backups zum Löschen gefunden." -LogFilePath $logFile
    }
}

# ======= Hauptlogik ========

# Startzeit loggen
Write-Log -Message "Hyper-V-Export-Skript gestartet." -LogFilePath $logFile

# Verzeichnis prüfen/erstellen
if (-not (Test-Path -Path $exportDir)) {
    Write-Log -Message "Erstelle Exportverzeichnis: $exportDir" -LogFilePath $logFile
    New-Item -Path $exportDir -ItemType Directory | Out-Null
}

# Alle VMs abrufen
try {
    $vms = Get-VM
    Write-Log -Message "Gefundene VMs: $($vms.Count)" -LogFilePath $logFile
} catch {
    Write-Log -Message "Fehler beim Abrufen der VMs: $_" -LogFilePath $logFile
    exit 1
}

# Jede VM exportieren
foreach ($vm in $vms) {
    try {
        $vmExportPath = Join-Path -Path $exportDir -ChildPath $vm.Name
        Write-Log -Message "Exportiere VM '$($vm.Name)' nach '$vmExportPath'..." -LogFilePath $logFile

        Export-VM -Name $vm.Name -Path $vmExportPath -Force
        Write-Log -Message "Export abgeschlossen: $($vm.Name)" -LogFilePath $logFile
    } catch {
        Write-Log -Message "Fehler beim Exportieren von VM '$($vm.Name)': $_" -LogFilePath $logFile
    }
}

# Alte Backups bereinigen
Cleanup-OldBackups -BackupDir $exportDir -MaxBackups $keepBackups

# Endzeit loggen
Write-Log -Message "Hyper-V-Export-Skript abgeschlossen." -LogFilePath $logFile

Erklärung des Skripts von ChatGPT

Das Skript von ChatGPT ist eine alternative Implementierung zum Exportieren von Hyper-V-VMs und verfolgt einen etwas anderen Ansatz als mein eigenes Skript. Es bietet einige zusätzliche Funktionen und Optimierungen, die ich hier erläutern möchte.

1. Verbesserte Protokollierung mit Zeitstempel

Die Funktion Write-Log wurde erweitert, um jedem Log-Eintrag einen Zeitstempel hinzuzufügen. Das Format lautet yyyy-MM-dd HH:mm:ss, was eine klare und übersichtliche Chronologie der Ereignisse ermöglicht.
Vorteil: Dadurch lassen sich Probleme einfacher nachvollziehen, da für jede Aktion der genaue Zeitpunkt protokolliert wird.

2. Fehlerhandling

Das Skript umfasst umfassendes Fehlerhandling mit try– und catch-Blöcken:

  • Wenn ein Fehler beim Abrufen der VMs auftritt, wird dies protokolliert, und das Skript wird abgebrochen (exit 1).
  • Fehler beim Exportieren einzelner VMs werden ebenfalls abgefangen und protokolliert. Dadurch wird das Skript nicht komplett abgebrochen, sondern es wird mit den nächsten VMs fortgefahren.
    Vorteil:
    Das Skript ist robuster und kann auch dann weiterarbeiten, wenn einzelne Operationen fehlschlagen.
3. Bereinigung alter Backups

Die Funktion Cleanup-OldBackups ist dafür verantwortlich, alte Backups zu entfernen:

  • Backups werden basierend auf ihrem CreationTime sortiert.
  • Es werden nur die ältesten Verzeichnisse gelöscht, die über der festgelegten Grenze ($keepBackups) liegen.
    Vorteil: Das Skript bleibt performant und speichert nur die erforderliche Anzahl von Backups, wodurch Speicherplatz effektiv verwaltet wird.
4. Dynamische VM-Exportpfade

Jede VM wird in ein separates Unterverzeichnis innerhalb des angegebenen Export-Verzeichnisses ($exportDir) exportiert. Der Pfad für jede VM wird mit Join-Path erstellt.
Vorteil: Diese Struktur sorgt für Übersichtlichkeit und vermeidet Konflikte bei den Exportdateien.

Was fehlt im Vergleich zu meinem Skript?

  • Datumsgesteuerte Verzeichnisse:
    Mein Skript erstellt für jeden Backup-Tag ein Verzeichnis mit dem aktuellen Datum. Dadurch kann man Backups besser zuordnen. Dies fehlt in der ChatGPT-Version.
  • Benachrichtigungen via ntfy:
    Die Möglichkeit, Benachrichtigungen an mein Smartphone zu senden, wurde in meinem Skript integriert, fehlt jedoch in der ChatGPT-Version. Dadurch erhält man keine direkte Rückmeldung über den Erfolg oder Misserfolg des Skripts.

Fazit

Die Entwicklung meines eigenen Backup-Skripts für Hyper-V hat gezeigt, wie wertvoll eine KI wie ChatGPT beim Programmieren sein kann. Sie liefert oft kreative Ansätze und kann helfen, Probleme schneller zu lösen. In meinem Fall hat die KI beispielsweise die Idee für eine Write-Log-Funktion beigesteuert, die mein Skript übersichtlicher und nachvollziehbarer macht.

Allerdings ist auch klar geworden, dass man sich nicht blind auf die KI verlassen kann. Das von ChatGPT generierte Skript hatte einige wichtige Funktionen ausgelassen, die für eine zuverlässige Sicherung essenziell sind. Das zeigt, dass eine KI zwar eine wertvolle Unterstützung sein kann, aber immer mit kritischem Blick geprüft und ergänzt werden muss.

Statt KI-Tools einfach nur als Code-Generator zu nutzen, sollte man sie vielmehr dazu verwenden, die eigenen Fähigkeiten zu verbessern. Indem man die Vorschläge analysiert, Schwachstellen erkennt und optimiert, kann man selbst als Entwickler wachsen und langfristig bessere Lösungen entwickeln.

Das von ChatGPT generierte Skript diente als Basis für meine weiterentwickelte Version. Ich habe es um die fehlenden ntfy Benachrichtungen ergänzt und anschließend weitere Modifikationen vorgenommen. Dazu gehören eine Main-Funktion sowie zwei neue Funktionen, die die Backups entweder zu einem TAR-Archiv packen oder als TAR.GZ-Archiv packen und komprimieren. Einige Funktionen sind durch Schalter-Variablen Optional aktivierbar indem man ihren Wert von „false“ auf „true setzt“.

Das fertige Skript stelle ich in meinem GitHub-Repository kostenlos zur Verfügung.

Dieser Artikel wurde mit Unterstützung von KI erstellt.


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert