I have updated my PowerShell Backup Script and want to provide you the new Release
I have added some New Feature, Staging Folder, Exclude Dirs, 7Zip Support and fixed some Bugs.
Please comment and rate the Script on TechNet
IMPORTANT, the Script is now on GitHub: https://github.com/Seidlm/PowerShell-Backup-Script
The Script
######################################################## # Name: BackupScript.ps1 # Creator: Michael Seidl aka Techguy # CreationDate: 21.01.2014 # LastModified: 19.02.2019 # Version: 1.4 # Doc: https://www.techguy.at/tag/backupscript/ # PSVersion tested: 3 and 4 # # Description: Copies the Bakupdirs to the Destination # You can configure more than one Backupdirs, every Dir # wil be copied to the Destination. A Progress Bar # is showing the Status of copied MB to the total MB # Only Change Variables in Variables Section # Change LoggingLevel to 3 an get more output in Powershell Windows # # # Beschreibung: Kopiert die BackupDirs in den Destination # Ordner. Es können mehr als nur ein Ordner angegeben # werden. Der Prozess wid mit einem Statusbar angezeigt # diese zeigt die kopieren MB im Vergleich zu den gesamten # MB an. # Nur die Werte unter Variables ändern # Ändere den Logginglevel zu 3 und erhalte die gesamte Ausgabe im PS Fenster # Version 1.4 # NEW: 7ZIP Support # FIX: Ordering at old Backup deletion # FIX: Exclude Dir is now working # NEW: Staging folder for ZIP # Version 1.3 - NEW: Send Mail Function # NEW: Backup Destination will be zipped # NEW: Exclude Dir # FIX: Logging Level # FIX: Delete old Folder by CreationTime # # Version 1.2 - FIX: Delete last Backup dirs, changed to support older PS Version # FIX: Fixed the Count in the Statusbar # FIX: Fixed Location Count in Statusbar # # Version 1.1 - CHANGE: Enhanced the Logging to a Textfile and write output, copy Log file to Backupdir # - FIX: Renamed some Variables an have done some cosmetic changes # - CHANGE: Define the Log Name in Variables # # Version 1.0 - RTM ######################################################## # # www.techguy.at # www.facebook.com/TechguyAT # www.twitter.com/TechguyAT # michael@techguy.at ######################################################## #Variables, only Change here $Destination="\\SVATHOME002\Backup$\NB BaseIT" #Copy the Files to this Location $Destination="C:\Users\seimi\Downloads" $Staging="C:\Users\seimi\Downloads\Staging" $ClearStaging=$true # When $true, Staging Dir will be cleared $Versions="5" #How many of the last Backups you want to keep $BackupDirs="C:\Users\seimi\OneDrive - Seidl Michael\0-Temp" #What Folders you want to backup $ExcludeDirs="C:\Users\seimi\OneDrive - Seidl Michael\0-Temp\Dir1","C:\Users\seimi\OneDrive - Seidl Michael\0-Temp\Dir2" #This list of Directories will not be copied $LogName="Log.txt" #Log Name $LoggingLevel="3" #LoggingLevel only for Output in Powershell Window, 1=smart, 3=Heavy $Zip=$true #Zip the Backup Destination $Use7ZIP=$true #Make sure it is installed $RemoveBackupDestination=$false #Remove copied files after Zip, only if $Zip is true $UseStaging=$true #only if you use ZIP, than we copy file to Staging, zip it and copy the ZIP to destination, like Staging, and to save NetworkBandwith #Send Mail Settings $SendEmail = $false # = $true if you want to enable send report to e-mail (SMTP send) $EmailTo = 'test@domain.com' #user@domain.something (for multiple users use "User01 <user01@example.com>" ,"User02 <user02@example.com>" ) $EmailFrom = 'from@domain.com' #matthew@domain $EmailSMTP = 'smtp.domain.com' #smtp server adress, DNS hostname. #STOP-no changes from here #STOP-no changes from here #Settings - do not change anything from here $ExcludeString="" #[string[]]$excludedArray = $ExcludeDirs -split "," foreach ($Entry in $ExcludeDirs) { $Temp="^"+$Entry.Replace("\","\\") $ExcludeString+=$Temp+"|" } $ExcludeString=$ExcludeString.Substring(0,$ExcludeString.Length-1) #$ExcludeString [RegEx]$exclude = $ExcludeString if ($UseStaging -and $Zip) { #Logging "INFO" "Use Temp Backup Dir" $Backupdir=$Staging +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\" } else { #Logging "INFO" "Use orig Backup Dir" $Backupdir=$Destination +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\" } #$BackupdirTemp=$Temp +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\" $Log=$Backupdir+$LogName $Log $Items=0 $Count=0 $ErrorCount=0 $StartDate=Get-Date #-format dd.MM.yyyy-HH:mm:ss #FUNCTION #Logging Function Logging ($State, $Message) { $Datum=Get-Date -format dd.MM.yyyy-HH:mm:ss if (!(Test-Path -Path $Log)) { New-Item -Path $Log -ItemType File | Out-Null } $Text="$Datum - $State"+":"+" $Message" if ($LoggingLevel -eq "1" -and $Message -notmatch "was copied") {Write-Host $Text} elseif ($LoggingLevel -eq "3") {Write-Host $Text} add-Content -Path $Log -Value $Text } #Create Backupdir Function Create-Backupdir { New-Item -Path $Backupdir -ItemType Directory | Out-Null sleep -Seconds 5 Logging "INFO" "Create Backupdir $Backupdir" } #Delete Backupdir Function Delete-Backupdir { $Folder=Get-ChildItem $Destination | where {$_.Attributes -eq "Directory"} | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1 Logging "INFO" "Remove Dir: $Folder" $Folder.FullName | Remove-Item -Recurse -Force } #Delete Zip Function Delete-Zip { $Zip=Get-ChildItem $Destination | where {$_.Attributes -eq "Archive" -and $_.Extension -eq ".zip"} | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1 Logging "INFO" "Remove Zip: $Zip" $Zip.FullName | Remove-Item -Recurse -Force } #Check if Backupdirs and Destination is available function Check-Dir { Logging "INFO" "Check if BackupDir and Destination exists" if (!(Test-Path $BackupDirs)) { return $false Logging "Error" "$BackupDirs does not exist" } if (!(Test-Path $Destination)) { return $false Logging "Error" "$Destination does not exist" } } #Save all the Files Function Make-Backup { Logging "INFO" "Started the Backup" $Files=@() $SumMB=0 $SumItems=0 $SumCount=0 $colItems=0 Logging "INFO" "Count all files and create the Top Level Directories" foreach ($Backup in $BackupDirs) { $colItems = (Get-ChildItem $Backup -recurse | Where-Object {$_.mode -notmatch "h"} | Measure-Object -property length -sum) $Items=0 $FilesCount += Get-ChildItem $Backup -Recurse | Where-Object {$_.mode -notmatch "h"} Copy-Item -Path $Backup -Destination $Backupdir -Force -ErrorAction SilentlyContinue $SumMB+=$colItems.Sum.ToString() $SumItems+=$colItems.Count } $TotalMB="{0:N2}" -f ($SumMB / 1MB) + " MB of Files" Logging "INFO" "There are $SumItems Files with $TotalMB to copy" foreach ($Backup in $BackupDirs) { $Index=$Backup.LastIndexOf("\") $SplitBackup=$Backup.substring(0,$Index) $Files = Get-ChildItem $Backup -Recurse | select * | Where-Object {$_.mode -notmatch "h" -and $_.fullname -notmatch $exclude} | select fullname #$_.mode -notmatch "h" -and foreach ($File in $Files) { $restpath = $file.fullname.replace($SplitBackup,"") try { Copy-Item $file.fullname $($Backupdir+$restpath) -Force -ErrorAction SilentlyContinue |Out-Null Logging "INFO" "$file was copied" } catch { $ErrorCount++ Logging "ERROR" "$file returned an error an was not copied" } $Items += (Get-item $file.fullname).Length $status = "Copy file {0} of {1} and copied {3} MB of {4} MB: {2}" -f $count,$SumItems,$file.Name,("{0:N2}" -f ($Items / 1MB)).ToString(),("{0:N2}" -f ($SumMB / 1MB)).ToString() $Index=[array]::IndexOf($BackupDirs,$Backup)+1 $Text="Copy data Location {0} of {1}" -f $Index ,$BackupDirs.Count Write-Progress -Activity $Text $status -PercentComplete ($Items / $SumMB*100) if ($File.Attributes -ne "Directory") {$count++} } } $SumCount+=$Count $SumTotalMB="{0:N2}" -f ($Items / 1MB) + " MB of Files" Logging "INFO" "----------------------" Logging "INFO" "Copied $SumCount files with $SumTotalMB" Logging "INFO" "$ErrorCount Files could not be copied" # Send e-mail with reports as attachments if ($SendEmail -eq $true) { $EmailSubject = "Backup Email $(get-date -format MM.yyyy)" $EmailBody = "Backup Script $(get-date -format MM.yyyy) (last Month).`nYours sincerely `Matthew - SYSTEM ADMINISTRATOR" Logging "INFO" "Sending e-mail to $EmailTo from $EmailFrom (SMTPServer = $EmailSMTP) " ### the attachment is $log Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -Body $EmailBody -SmtpServer $EmailSMTP -attachment $Log } } #create Backup Dir Create-Backupdir Logging "INFO" "----------------------" Logging "INFO" "Start the Script" #Check if Backupdir needs to be cleaned and create Backupdir $Count=(Get-ChildItem $Destination | where {$_.Attributes -eq "Directory"}).count Logging "INFO" "Check if there are more than $Versions Directories in the Backupdir" if ($count -gt $Versions) { Delete-Backupdir } $CountZip=(Get-ChildItem $Destination | where {$_.Attributes -eq "Archive" -and $_.Extension -eq ".zip"}).count Logging "INFO" "Check if there are more than $Versions Zip in the Backupdir" if ($CountZip -gt $Versions) { Delete-Zip } #Check if all Dir are existing and do the Backup $CheckDir=Check-Dir if ($CheckDir -eq $false) { Logging "ERROR" "One of the Directory are not available, Script has stopped" } else { Make-Backup $Enddate=Get-Date #-format dd.MM.yyyy-HH:mm:ss $span = $EndDate - $StartDate $Minutes=$span.Minutes $Seconds=$Span.Seconds Logging "INFO" "Backupduration $Minutes Minutes and $Seconds Seconds" Logging "INFO" "----------------------" Logging "INFO" "----------------------" if ($Zip) { Logging "INFO" "Compress the Backup Destination" if ($Use7ZIP) { Logging "INFO" "Use 7ZIP" if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {Logging "WARNING" "7Zip not found"} set-alias sz "$env:ProgramFiles\7-Zip\7z.exe" #sz a -t7z "$directory\$zipfile" "$directory\$name" if ($UseStaging -and $Zip) { $Zip=$Staging+("\"+$Backupdir.Replace($Staging,'').Replace('\','')+".zip") sz a -t7z $Zip $Backupdir Logging "INFO" "Move Zip to Destination" Move-Item -Path $Zip -Destination $Destination if ($ClearStaging) { Logging "INFO" "Clear Staging" Get-ChildItem -Path $Staging -Recurse -Force | remove-item -Confirm:$false -Recurse } } else { sz a -t7z ($Destination+("\"+$Backupdir.Replace($Destination,'').Replace('\','')+".zip")) $Backupdir } } else { Logging "INFO" "Use Powershell Compress-Archive" Compress-Archive -Path $Backupdir -DestinationPath ($Destination+("\"+$Backupdir.Replace($Destination,'').Replace('\','')+".zip")) -CompressionLevel Optimal -Force } If ($RemoveBackupDestination) { Logging "INFO" "Backupduration $Minutes Minutes and $Seconds Seconds" #Remove-Item -Path $BackupDir -Force -Recurse get-childitem -Path $BackupDir -recurse -Force | remove-item -Confirm:$false -Recurse get-item -Path $BackupDir | remove-item -Confirm:$false -Recurse } } } Write-Host "Press any key to close ..." $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Download is available at TechNet Gallery: http://gallery.technet.microsoft.com/PowerShell-Backup-Script-956f312c
All my TechNet Downloads: 1jrYQoA
PLEASE RATE AND COMMENT.
Michael Seidl aka Techguy
is there a way to exclude file types? If this is discussed elsewhere please point me to that blog.
Hi, sure, would be an easy one, will be added to the Backlog for the upcoming release
it’s very good for mi.
You have an additional “+” BEFORE the “=” on line 91. Here:
$ExcludeString+=$Temp+”|”
It makes the script run but actually fail to do anything useful. When I removed this + everything ran as expected.
All the best.
Hi,
I’m new in this Powershell stuff. I adapt your code so I can do the backup of our server but the only information that I can see is the log file in the staging folder and in the Destination. I’m not able to retried any information from the files that I want to save. In the destination folder I just can see the log and the backup zip and in this one it’s empty.
Thank’s for a great tool. Not sure I missed something but I do not see the tool is backing up hidden files like user Appdata folder (using 1.3 now but will update)
OK, 1.4 seems to be stable for this, I do not see the problem there. I other words – other folders was empty periodically as well in 1.3, not only hidden ones. In 1.4 I have not seen it yet.
Thanks
Firstly, thank you for a great script.
Is there a way when backing up multiple locations to put them in their own folder inside the destination?
For example:
Backup c:\users\bill\documents
Backup c:\users\john\documents
currently it would place all documents in the s:\backups\ and mix both users documents
Would like it to take the parent folder name (up 1 level) and use that as the destination. for example:
S:\backups\bill\documents
S:\backups\john\documents
This way Bill and John are separate folders and can find files easier.
Hi,
thanks for your comment, I will add this to the feature list for the next release
I like your script, you have done a great Job.
After monthes of waiting for AVM supporting SMB V2/3 now i have tested making backup using “backupscript V1.4”.
I use Windows 10/10Pro V1909 backing up to FRITZ.NAS.
A first impression it seems to work well, further testing has to be done.
But there are some issues:
Progress-bar:
– counting files works not exactly (i.e. Copied 519 of 481 files with…)
– counting dataspace works not exactly (i.e. …with 2.176,43 MB of 1.169,58 MB of Files)
Log-file:
– some files are marked as “ERROR: returned an error an was not copied” but are correctly copied to media
– Showing backupduration INFO: Backupduration 0 Hours 6 Minutes and 20 Seconds
“24.11.2019-15:47:03 – INFO: There are 111152 Files with 303.772,75 MB of Files to copy
25.11.2019-01:39:37 – INFO: Backupduration 53 Minutes and 10 Seconds”
best regards
Thank you for this. Its great, I am however trying to add some code to make a change.
Need to addin where script will check the \Users folder for all listed users.
Then prompt you to choose which user Dir to Backup.
Upon choosing, then auto fill in the user for ‘seimi”
Is that possible?
Great script
Could you replace 7zip with powershell Compress-Archive to avoid installing 7zip
Would be greatly appreciated
Hi, please see the latest release on github, you can us built in Zip also
Hi Michael,
Your script is very good and I have learned some PS in the process. The only thing I can’t work out is how I can back up a folder and it’s content to a less complex path.
e.g.
C:\Data\B\B1\B11\B111 ***I just want this set of folders and subs***
I would like to be able to save the files to E:\Backups\B_yyyymmdd with the same sub folders but what I get is;
E:\Backups\Backup_yyyymmdd-Random number\Data\B\B1, etc
I have figured out how to remove the random numbers but not how to alter the path to how I’d like. Could you offer some advise please, I think I’m missing some vital knowledge as to how the paths are built.
Hi, please post your wish at the Github Repo
hi, how use “Send Email Settings”? I set email from, email to and $EmailSMTP = ‘smtp.gmail.com’ and it not working. How can i use it?
I ran into an issue that I was able to fix.
I use PowerShellISE so I can interactively work on scripts and run them easily.
At the end of the script, I get:
Exception calling “ReadKey” with “1” argument(s): “The method or operation is not implemented.”
At C:\Users\\Downloads\BackupScript.ps1:356 char:1
+ $x = $host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotImplementedException
After some searching, I found that ReadKey does not work in PowerShellISE.
A solution I found that works in PowerShell and PowerShellISE was at
https://stackoverflow.com/questions/20886243/press-any-key-to-continue
I added the following function:
Function pause ($message)
{
# Check if running Powershell ISE
if ($psISE)
{
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show(“$message”)
}
else
{
Write-Host “$message” -ForegroundColor Yellow
$x = $host.ui.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
}
}
and replaced the following from the original script, not in the function
$x = $host.ui.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
with
pause “Press any key to close …”
Works like a charm.
As the ExcludeDirs are regex parsed I have an issue with directories that contain + characters. I tried to escape them with \ but this did not lead to a success.
Can you please advise how to properly escape characters?
Thanks
Please get the latest release from GitHub
Hello Michael,
great thanks for your Script.
Unfortunately I have a problem.
The logfile say to me, that the script find 5 files with 0.04 MB to copy but after that, it copies 2 files with 0.00 MB and no file is in the backup folder.
What is my problem in the script?
Thanks allot.
Best regards Alex
Dear Michael,
I tested the script and it works fine, with exception of some minor flaws. May I ask if you are still maintaining it on GitHub? It would be great if you can have a look at issue #9 “Exclude files does not work properly”. Thanks for the cool script.
Best
Jule
Thanks, will take a Look
Hello, I tried your masterpiece, but aleas I’m totally noob at PS.
I’m stuck with :
Count all files and create the Top Level Directories
Impossible d’appeler une méthode dans une expression Null.
Au caractère I:\p15.ps1:204 : 9
+ $SumMB += $colItems.Sum.ToString()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation : (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
There are 0 Files with 0,00 MB of Files to copy
The settings I used….
#Variables, only Change here
$Destination = “i:\ps1″
#”\\SVATHOME002\Backup$\NB BaseIT” #Copy the Files to this Location
$Staging = “i:\ps1\Staging”
$ClearStaging = $true # When $true, Staging Dir will be cleared
$Versions = “3” #How many of the last Backups you want to keep
$BackupDirs = “F:\GcodeF”
#”C:\Users\seimi\Documents” #What Folders you want to backup
$ExcludeDirs = #This list of Directories will not be copied
($env:SystemDrive + “\Users\.*\AppData\Local”)
#($env:SystemDrive + “\Users\.*\AppData\LocalLow”),
#”C:\Users\seimi\OneDrive – Seidl Michael\0-Temp”,
#”C:\Users\seimi\OneDrive – Seidl Michael\0-Temp\Dir2″
$LogfileName = “Log” #Log Name
$LoggingLevel = “3” #LoggingLevel only for Output in Powershell Window, 1=smart, 3=Heavy
$Zip = $true #Zip the Backup Destination
$Use7ZIP = $false #Make sure it is installed
$RemoveBackupDestination = $true #Remove copied files after Zip, only if $Zip is true
$UseStaging = $false #only if you use ZIP, than we copy file to Staging, zip it and copy the ZIP to destination, like Staging, and to save NetworkBandwith
#Send Mail Settings
$SendEmail = $false # = $true if you want to enable send report to e-mail (SMTP send)
$EmailTo = ‘test@domain.com’ #user@domain.something (for multiple users use “User01 <user01@example.com>” ,”User02 <user02@example.com>” )
$EmailFrom = ‘from@domain.com’ #matthew@domain
$EmailSMTP = ‘smtp.domain.com’ #smtp server adress, DNS hostname.
Your help would be greatly appreciated (I tried newest 1.5 version with same error…)
Pingback: Powershell Backup Script – TricksDream
Pingback: Powershell Backup Script - SecuredGuide