PowerShell Techguy.at

PowerShell BackupScript

PowerShell

Ich hab mal wieder ein Script geschrieben welches mir meine Daten auf dem Notebook auf eine externe HDD sichert.

Ich war letztens 2 Wochen beruflich in China unterwegs und hatte etwas Zeit über mein Backup nachzudenken. Auch wenn ich alle Daten in der Cloud, also Skydrive oder Dropbox speichere, wollte ich dennoch ein offline Backup haben.

Aktuell muss ich nur 2 Verzeichnisse sichern, meinen Benutzerordner und ein Programm, welches ich nicht so konfigurieren konnte das es die Daten in meinem Profil speichert.

Was würde also besser passen als ein PowerShell Script zu erstellen welches mir diese Daten sichert.

Das Script

Zu Beginn natürlich wieder die Variablen, die konfiguriert werden können.

$Destination ist das Ziel für euer Backup
$Versions gibt die Zahl der Backups an die behalten werden, alles was diese Zahl überschreitet wird im Ordner $Destination gelöscht
$Backupdirs gibt die Verzeichnisse an die gesichert werden soll, hier können auch mehr als 1 Verzeichnis angegeben werden

$Destination="F:\_Backup" #Copy the Files to this Location
$Versions="10" #How many of the ölast Backups you want to keep
$BackupDirs="C:\Users\Michael\AppData\Roaming\TaskUnifier","C:\Users\Michael" #What Folders you want to backup

Danach kommen unsere Einstellungen, in denen wir den Namen für unser Backupdir suchen und ein paar Variablen definieren

$Backupdir=$Destination +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\"
$Items=0
$Count=0

Danach folgt der Code, zuerst überprüfen wir ob alle Verzeichnisse auch existieren, danach erstellen wir das Backup Verzeichnis, sollte es hier zu Fehlern kommen wir das Script gestoppt.

Jedoch wenn alles funktioniert wird mit der Sicherung begonnen und euch wir ein Statusbalken angezeigt.

#Function
#Create Backupdir
Function Create-Backupdir {
    Write-Host "Create Backupdir" $Backupdir
    New-Item -Path $Backupdir -ItemType Directory | Out-Null
}

#Delete Backupdir
Function Delete-Backupdir {
    Write-Host "Delete old Backup"
    $Delete=$Count-$Versions+1
    Get-ChildItem $Destination -Directory | Sort-Object -Property $_.LastWriteTime -Descending  | Select-Object -First $Delete | Remove-Item -Recurse -Force
}

#Check if Backupdirs and Destination is available
function Check-Dir {
    if (!(Test-Path $BackupDirs)) {
        return $false
    }
    if (!(Test-Path $Destination)) {
        return $false
    }
}

#Save all the Files
Function Make-Backup {
    $Files=@()
    $SumItem=0

    foreach ($Backup in $BackupDirs) {
        $colItems = (Get-ChildItem $Backup -recurse | Measure-Object -property length -sum) 
        #"{0:N2}" -f ($colItems.sum / 1MB) + " MB of Files"
        $Items=0
        $FilesCount += Get-ChildItem $Backup -Recurse | Where-Object {$_.mode -notmatch "h"}  
        Copy-Item -Path $Backup -Destination $Backupdir -Force -ErrorAction SilentlyContinue
        $SumItem+=$colItems.Sum.ToString()
        $SumItems+=$colItems.Count
    }

    $TotalMB="{0:N2}" -f ($SumItem / 1MB) + " MB of Files"
    Write-Host "There will be"$TotalMB "copied and there are"$filesCount.Count "files to copy"

    foreach ($Backup in $BackupDirs) {
        $Index=$Backup.LastIndexOf("\")
        $SplitBackup=$Backup.substring(0,$Index)
        $Files = Get-ChildItem $Backup -Recurse | Where-Object {$_.mode -notmatch "h"} 
        foreach ($File in $Files) {
            $restpath = $file.fullname.replace($SplitBackup,"")
            Copy-Item  $file.fullname $($Backupdir+$restpath) -Force -ErrorAction SilentlyContinue |Out-Null
            $Items += (Get-item $file.fullname).Length
            $status = "Copy file {0} of {1} and copied {3} MB of {4} MB: {2}" -f $count,$filesCount.Count,$file.Name,("{0:N2}" -f ($Items / 1MB)).ToString(),("{0:N2}" -f ($SumItem / 1MB)).ToString()
            $Text="Copy data Location {0} of {1}" -f $BackupDirs.Rank ,$BackupDirs.Count
            Write-Progress -Activity $Text $status -PercentComplete ($Items / $SumItem*100)  
            $count++
        }
    }
    $SumCount+=$Count

    Write-Host "Copied" $SumCount "files with" ("{0:N2}" -f ($Items / 1MB)).ToString()"of MB"
}

#Check if Backupdir needs to be cleaned and create Backupdir
$Count=(Get-ChildItem $Destination -Directory).count
if ($count -lt $Versions) {
    Create-Backupdir
} else {
    Delete-Backupdir
    Create-Backupdir
}

#Check if all Dir are existing and do the Backup
$CheckDir=Check-Dir
if ($CheckDir -eq $false) {
    Write-Host "One of the Directory are not available, Script has stopped"
} else {
    Make-Backup
}

Write-Host "Press any key to close ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

Download auf der TechNet Gallery: http://gallery.technet.microsoft.com/PowerShell-Backup-Script-956f312c

Solltet ihr Fragen, Wünsche oder Probleme mit dem Script haben, schreibt mir ein Kommentar.

Alle meine TechNet Gallery Downloads findet ihr hier: 1jrYQoA

BITTE BEWERTE MEINE DOWNLOADS IN DER TECHNET GALLERY UND SAGT MIR WAS IHR EUCH VON DER NÄCHSTEN VERSION WÜNSCHT.

Michael Seidl aka Techguy

12 thoughts on “PowerShell BackupScript”

  1. Hallo Michael,

    zunächst vielen Dank für das Script 😉 Sehr hilfreich.

    Mir sind jedoch 2 Fehler aufgefallen:

    1. Zählen der Verzeichnissen im Backup-Ziel funktioniert nicht (PowerShell Fehlermledung)

    Falsch:
    $Count=(Get-ChildItem $Destination -Directory).count

    Richtig:
    $Count=(Get-ChildItem $Destination | where {$_.Attributes -eq ‘Directory’}).count

    Den Parameter -Directory gibt es nicht. Demnach muss im gesamten Script
    –> Get-ChildItem $Destination -Directory
    durch
    –> Get-ChildItem $Destination | where {$_.Attributes -eq ‘Directory’}
    ersetzt werden.

    2. Beim Löschen der Backups wird immer das jüngste Backup gelöscht und nicht das älteste

    Falsch:
    Get-ChildItem $Destination -Directory | Sort-Object -Property $_.LastWriteTime -Descending | Select-Object -First $Delete | Remove-Item -Recurse -Force

    Richtig:
    Get-ChildItem $Destination | where {$_.Attributes -eq ‘Directory’} | Sort-Object -Property @{Expression={$_.LastWriteTime};Ascending=$true} | Select-Object -First $Delete | Remove-Item -Recurse -Force

    Die Sortierung, so wie du sie geschrieben hast, funktioniert nicht (es wird, warum auch immer, nicht nach LastWriteTime, sondern nach Ordnernamen sortiert.
    Es muss also
    –> Sort-Object -Property $_.LastWriteTime -Descending
    durch
    –> Sort-Object -Property @{Expression={$_.LastWriteTime};Ascending=$true}
    ersetzt werden.

    Gruß
    deparcus

  2. Hallo,

    ich nutze Version 2.0. Könnte eventuell daran liegen.
    Habe nicht gesehen, dass es eine neue Version deines Scripts gibt 😉 Danke, werde mir sie mal anschauen.

    Gruß
    deparcus

  3. Hallo,

    habe mir dein Script in Version 1.1 mal angeschaut und getestet.
    Die von mir beschriebenen Fehler sind dort auch drin.
    Fehler 1 mag an meiner PowerShell-Version liegen (wobei ich eine Lösung die nahezu unabhängig von der Version ist eleganter finde), aber Fehler 2 offenbar nicht, da hier die Sortierung nicht wie gewollt funktioniert.

    Gruß
    deparcus

  4. Hi,
    super Sache mit dem Backup-Script; habe versucht das auf meine Anforderungen anzupassen; grds. läuft es schon. Probs habe ich mit speziellen Verzeichnissen: z. B. user\appdata\roaming; es werden da Dateien ausgelassen. Weisst Du warum?

    VG
    Jens

  5. Hallo,

    das Script is so gebaut, das Verzeichnisse welche keinen Zugriff gewähren, ausgelassen werden.
    ich denke das der ausführende User kein Recht hat, auf das Verzeichnis zuzugreifen.

  6. Hi,
    aber beim manuellen Kopieren unter Windows werden alle Dateien kopiert; es muss also einen Unterschied geben, entweder hins. der Berechtigung bei der Ausführung des Scripts oder im Copy-Befehl selbst.

    Danke für die Hilfe!
    Gruß
    Jens

  7. Ergänzung: es liegt an Dateien mit Sonderzeichen (bei mir eckige Klammern); leider kann man ja nie ausschließen, dass solche Dateien im Sicherungsbestand sind; die werden dann stillschweigend übergangen. Hab noch keine Lösung gefunden.

    VG
    jens

  8. Kann man ein Batch-Skript zu einem PowerShell-Skript umschreiben?

    REM Datum
    set jahr=%date:~-4%
    set monat=%date:~-7,2%
    set tag=%date:~-10,2%
    set mytime=%time%
    set stunde=%mytime:~0,2%
    if “%stunde:~0,1%”==” ” set stunde=0%stunde:~1,1%
    set minute=%time:~3,2%
    set datum=%jahr%-%monat%-%tag%-%stunde%-%minute%
    “C:\Program Files (x86)\Snapshot\snapshot64.exe” HD1:* Z:\Server01-Backup\%datum%\$computername$HD.SNA -R -L1490 -W –AllWriters –logfile:C:\Snapshot\logfile.txt –createdir
    copy C:\Snapshot\logfile.txt Z:\Server01-Backup\Backup-Logs

    Wie macht man das mit dem Erstellen eines Ordners in PowerShell?

    Gruß Daniel

  9. Hallo,
    super Lösung, vielen Dank.
    Die einzige Änderung, die notwendig ist: Bei Verwendung von 7zip ist die Dateiendung nicht “.zip”, sondern “.7z”.
    Die Aktionen des Scripts habe sogar ich als Powershell-Laie verstanden, die notwendigen Anpassungen der Verzeichnisse waren einfach.

    Seit der Erstellung des Scripts hat sich allerdings bei der Festplattengeschwindigkeit einiges getan, vor allem die Verwendung von SSD’s als Festplatte.
    Es könnte daher schneller sein, zuerst die Komprimierung von den Orginaldateien vorzunehmen, und danach nur noch das zip-file zu verschieben.
    Optimierung: Übernahme der Verzeichnisse (BackupDirs, Destination,Exclude) als Parameter mit param()

    Nochmals vielen Dank und viel Erfolg,
    Rudolf Fiedler

Leave a Comment

Your email address will not be published. Required fields are marked *

*