├── .github └── FUNDING.yml ├── BackupScript_v1.ps1 ├── BackupScript_v2.ps1 ├── LICENSE └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [seidlm] 4 | -------------------------------------------------------------------------------- /BackupScript_v1.ps1: -------------------------------------------------------------------------------- 1 | ######################################################## 2 | # Name: BackupScript.ps1 3 | # Creator: Michael Seidl aka Techguy 4 | # CreationDate: 21.01.2014 5 | # LastModified: 31.03.2020 6 | # Version: 1.5 7 | # Doc: http://www.techguy.at/tag/backupscript/ 8 | # GitHub: https://github.com/Seidlm/PowerShell-Backup-Script 9 | # PSVersion tested: 3 and 4 10 | # 11 | # PowerShell Self Service Web Portal at www.au2mator.com/PowerShell 12 | # 13 | # 14 | # Description: Copies the Bakupdirs to the Destination 15 | # You can configure more than one Backupdirs, every Dir 16 | # wil be copied to the Destination. A Progress Bar 17 | # is showing the Status of copied MB to the total MB 18 | # Only Change Variables in Variables Section 19 | # Change LoggingLevel to 3 an get more output in Powershell Windows 20 | # 21 | # 22 | ##########################ync############################## 23 | # 24 | # www.techguy.at 25 | # www.facebook.com/TechguyAT 26 | # www.twitter.com/TechguyAT 27 | # michael@techguy.at 28 | 29 | # 30 | # 31 | ######################################################## 32 | 33 | #Variables, only Change here 34 | $Destination = "C:\temp\_BAckup" #Copy the Files to this Location 35 | $Staging = "C:\temp\_Staging" 36 | $ClearStaging = $true # When $true, Staging Dir will be cleared 37 | $Versions = "15" #How many of the last Backups you want to keep 38 | $BackupDirs = "C:\Users\seimi\Documents","C:\Program Files (x86)\Common Files" #What Folders you want to backup 39 | 40 | $ExcludeDirs = ($env:SystemDrive + "\Users\.*\AppData\Local"),"C:\Program Files (x86)\Common Files\Adobe" #This list of Directories will not be copied 41 | 42 | 43 | $LogfileName = "Log" #Log Name 44 | $LoggingLevel = "3" #LoggingLevel only for Output in Powershell Window, 1=smart, 3=Heavy 45 | $Zip = $false #Zip the Backup Destination 46 | $Use7ZIP = $false #Make sure it is installed 47 | $RemoveBackupDestination = $true #Remove copied files after Zip, only if $Zip is true 48 | $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 49 | 50 | 51 | 52 | #Send Mail Settings 53 | $SendEmail = $false # = $true if you want to enable send report to e-mail (SMTP send) 54 | $EmailTo = 'test@domain.com' #user@domain.something (for multiple users use "User01 <user01@example.com>" ,"User02 <user02@example.com>" ) 55 | $EmailFrom = 'from@domain.com' #matthew@domain 56 | $EmailSMTP = 'smtp.domain.com' #smtp server adress, DNS hostname. 57 | 58 | 59 | #STOP-no changes from here 60 | #STOP-no changes from here 61 | #Settings - do not change anything from here 62 | 63 | $ExcludeString = "" 64 | foreach ($Entry in $ExcludeDirs) { 65 | #Exclude the directory itself 66 | $Temp = "^" + $Entry.Replace("\", "\\") + "$" 67 | $ExcludeString += $Temp + "|" 68 | 69 | #Exclude the directory's children 70 | $Temp = "^" + $Entry.Replace("\", "\\") + "\\.*" 71 | $ExcludeString += $Temp + "|" 72 | } 73 | $ExcludeString = $ExcludeString.Substring(0, $ExcludeString.Length - 1) 74 | [RegEx]$exclude = $ExcludeString 75 | 76 | if ($UseStaging -and $Zip) { 77 | #Logging "INFO" "Use Temp Backup Dir" 78 | $Backupdir = $Staging + "\Backup-" + (Get-Date -format yyyy-MM-dd) + "-" + (Get-Random -Maximum 100000) + "\" 79 | } 80 | else { 81 | #Logging "INFO" "Use orig Backup Dir" 82 | $Backupdir = $Destination + "\Backup-" + (Get-Date -format yyyy-MM-dd) + "-" + (Get-Random -Maximum 100000) + "\" 83 | } 84 | 85 | 86 | 87 | #$BackupdirTemp=$Temp +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\" 88 | $logPath = $Destination 89 | 90 | $Items = 0 91 | $Count = 0 92 | $ErrorCount = 0 93 | $StartDate = Get-Date #-format dd.MM.yyyy-HH:mm:ss 94 | 95 | #FUNCTION 96 | #Logging 97 | 98 | function Write-au2matorLog { 99 | [CmdletBinding()] 100 | param 101 | ( 102 | [ValidateSet('DEBUG', 'INFO', 'WARNING', 'ERROR')] 103 | [string]$Type, 104 | [string]$Text 105 | ) 106 | 107 | # Set logging path 108 | if (!(Test-Path -Path $logPath)) { 109 | try { 110 | $null = New-Item -Path $logPath -ItemType Directory 111 | Write-Verbose ("Path: ""{0}"" was created." -f $logPath) 112 | } 113 | catch { 114 | Write-Verbose ("Path: ""{0}"" couldn't be created." -f $logPath) 115 | } 116 | } 117 | else { 118 | Write-Verbose ("Path: ""{0}"" already exists." -f $logPath) 119 | } 120 | [string]$logFile = '{0}\{1}_{2}.log' -f $logPath, $(Get-Date -Format 'yyyyMMdd'), $LogfileName 121 | $logEntry = '{0}: <{1}> <{2}> {3}' -f $(Get-Date -Format dd.MM.yyyy-HH:mm:ss), $Type, $PID, $Text 122 | 123 | try { Add-Content -Path $logFile -Value $logEntry } 124 | catch { 125 | Start-sleep -Milliseconds 50 126 | Add-Content -Path $logFile -Value $logEntry 127 | } 128 | if ($LoggingLevel -eq "3") { Write-Host $Text } 129 | 130 | 131 | } 132 | 133 | 134 | #Create Backupdir 135 | Function New-Backupdir { 136 | New-Item -Path $Backupdir -ItemType Directory | Out-Null 137 | Start-sleep -Seconds 5 138 | Write-au2matorLog -Type Info -Text "Create Backupdir $Backupdir" 139 | } 140 | 141 | #Delete Backupdir 142 | Function Remove-Backupdir { 143 | $Folder = Get-ChildItem $Destination | where { $_.Attributes -eq "Directory" } | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1 144 | 145 | Write-au2matorLog -Type Info -Text "Remove Dir: $Folder" 146 | 147 | $Folder.FullName | Remove-Item -Recurse -Force 148 | } 149 | 150 | 151 | #Delete Zip 152 | Function Remove-Zip { 153 | $Zip = Get-ChildItem $Destination | where { $_.Attributes -eq "Archive" -and $_.Extension -eq ".zip" } | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1 154 | 155 | Write-au2matorLog -Type Info -Text "Remove Zip: $Zip" 156 | 157 | $Zip.FullName | Remove-Item -Recurse -Force 158 | } 159 | 160 | #Check if Backupdirs and Destination is available 161 | function Check-Dir { 162 | Write-au2matorLog -Type Info -Text "Check if BackupDir and Destination exists" 163 | if (!(Test-Path $BackupDirs)) { 164 | return $false 165 | Write-au2matorLog -Type Error -Text "$BackupDirs does not exist" 166 | } 167 | if (!(Test-Path $Destination)) { 168 | return $false 169 | Write-au2matorLog -Type Error -Text "$Destination does not exist" 170 | } 171 | } 172 | 173 | #Save all the Files 174 | Function Make-Backup { 175 | Write-au2matorLog -Type Info -Text "Started the Backup" 176 | $BackupDirFiles = @{ } #Hash of BackupDir & Files 177 | $Files = @() 178 | $SumMB = 0 179 | $SumItems = 0 180 | $SumCount = 0 181 | $colItems = 0 182 | Write-au2matorLog -Type Info -Text "Count all files and create the Top Level Directories" 183 | 184 | foreach ($Backup in $BackupDirs) { 185 | # Get recursive list of files for each Backup Dir once and save in $BackupDirFiles to use later. 186 | # Optimize performance by getting included folders first, and then only recursing files for those. 187 | # Use -LiteralPath option to work around known issue with PowerShell FileSystemProvider wildcards. 188 | # See: https://github.com/PowerShell/PowerShell/issues/6733 189 | 190 | $Files = Get-ChildItem -LiteralPath $Backup -recurse -Attributes D+!ReparsePoint, D+H+!ReparsePoint -ErrorVariable +errItems -ErrorAction SilentlyContinue | 191 | ForEach-Object -Process { Add-Member -InputObject $_ -NotePropertyName "ParentFullName" -NotePropertyValue ($_.FullName.Substring(0, $_.FullName.LastIndexOf("\" + $_.Name))) -PassThru -ErrorAction SilentlyContinue } | 192 | Where-Object { $_.FullName -notmatch $exclude -and $_.ParentFullName -notmatch $exclude } | 193 | Get-ChildItem -Attributes !D -ErrorVariable +errItems -ErrorAction SilentlyContinue | Where-Object { $_.DirectoryName -notmatch $exclude } 194 | $BackupDirFiles.Add($Backup, $Files) 195 | 196 | $colItems = ($Files | Measure-Object -property length -sum) 197 | $Items = 0 198 | Copy-Item -LiteralPath $Backup -Destination $Backupdir -Force -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch $exclude } 199 | $SumMB += $colItems.Sum.ToString() 200 | $SumItems += $colItems.Count 201 | } 202 | 203 | $TotalMB = "{0:N2}" -f ($SumMB / 1MB) + " MB of Files" 204 | Write-au2matorLog -Type Info -Text "There are $SumItems Files with $TotalMB to copy" 205 | 206 | #Log any errors from above from building the list of files to backup. 207 | [System.Management.Automation.ErrorRecord]$errItem = $null 208 | foreach ($errItem in $errItems) { 209 | Write-au2matorLog -Type ERROR -Text ("Skipping `"" + $errItem.TargetObject + "`" Error: " + $errItem.CategoryInfo) 210 | } 211 | Remove-Variable errItem 212 | Remove-Variable errItems 213 | 214 | foreach ($Backup in $BackupDirs) { 215 | $Index = $Backup.LastIndexOf("\") 216 | $SplitBackup = $Backup.substring(0, $Index) 217 | $Files = $BackupDirFiles[$Backup] 218 | 219 | foreach ($File in $Files) { 220 | $restpath = $file.fullname.replace($SplitBackup, "") 221 | try { 222 | # Use New-Item to create the destination directory if it doesn't yet exist. Then copy the file. 223 | New-Item -Path (Split-Path -Path $($Backupdir + $restpath) -Parent) -ItemType "directory" -Force -ErrorAction SilentlyContinue | Out-Null 224 | Copy-Item -LiteralPath $file.fullname $($Backupdir + $restpath) -Force -ErrorAction SilentlyContinue | Out-Null 225 | Write-au2matorLog -Type Info -Text $("'" + $File.FullName + "' was copied") 226 | } 227 | catch { 228 | $ErrorCount++ 229 | Write-au2matorLog -Type Error -Text $("'" + $File.FullName + "' returned an error and was not copied") 230 | } 231 | $Items += (Get-item -LiteralPath $file.fullname).Length 232 | $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() 233 | $Index = [array]::IndexOf($BackupDirs, $Backup) + 1 234 | $Text = "Copy data Location {0} of {1}" -f $Index , $BackupDirs.Count 235 | Write-Progress -Activity $Text $status -PercentComplete ($Items / $SumMB * 100) 236 | if ($File.Attributes -ne "Directory") { $count++ } 237 | } 238 | } 239 | $SumCount += $Count 240 | $SumTotalMB = "{0:N2}" -f ($Items / 1MB) + " MB of Files" 241 | Write-au2matorLog -Type Info -Text "----------------------" 242 | Write-au2matorLog -Type Info -Text "Copied $SumCount files with $SumTotalMB" 243 | Write-au2matorLog -Type Info -Text "$ErrorCount Files could not be copied" 244 | 245 | 246 | # Send e-mail with reports as attachments 247 | if ($SendEmail -eq $true) { 248 | $EmailSubject = "Backup Email $(get-date -format MM.yyyy)" 249 | $EmailBody = "Backup Script $(get-date -format MM.yyyy) (last Month).`nYours sincerely `Matthew - SYSTEM ADMINISTRATOR" 250 | Write-au2matorLog -Type Info -Text "Sending e-mail to $EmailTo from $EmailFrom (SMTPServer = $EmailSMTP) " 251 | ### the attachment is $log 252 | Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -Body $EmailBody -SmtpServer $EmailSMTP -attachment $Log 253 | } 254 | } 255 | 256 | #create Backup Dir 257 | 258 | New-Backupdir 259 | Write-au2matorLog -Type Info -Text "----------------------" 260 | Write-au2matorLog -Type Info -Text "Start the Script" 261 | 262 | #Check if Backupdir needs to be cleaned and create Backupdir 263 | $Count = (Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Directory" }).count 264 | Write-au2matorLog -Type Info -Text "Check if there are more than $Versions Directories in the Backupdir" 265 | 266 | if ($count -gt $Versions) { 267 | Write-au2matorLog -Type Info -Text "Found $count Backups" 268 | Remove-Backupdir 269 | } 270 | 271 | 272 | $CountZip = (Get-ChildItem $Destination | where { $_.Attributes -eq "Archive" -and $_.Extension -eq ".zip" }).count 273 | Write-au2matorLog -Type Info -Text "Check if there are more than $Versions Zip in the Backupdir" 274 | 275 | if ($CountZip -gt $Versions) { 276 | 277 | Remove-Zip 278 | 279 | } 280 | 281 | #Check if all Dir are existing and do the Backup 282 | $CheckDir = Check-Dir 283 | 284 | if ($CheckDir -eq $false) { 285 | Write-au2matorLog -Type Error -Text "One of the Directories are not available, Script has stopped" 286 | } 287 | else { 288 | Make-Backup 289 | 290 | $Enddate = Get-Date #-format dd.MM.yyyy-HH:mm:ss 291 | $span = $EndDate - $StartDate 292 | $Duration = $("Backup duration " + $span.Hours.ToString() + " hours " + $span.Minutes.ToString() + " minutes " + $span.Seconds.ToString() + " seconds") 293 | 294 | Write-au2matorLog -Type Info -Text "$Duration" 295 | Write-au2matorLog -Type Info -Text "----------------------" 296 | Write-au2matorLog -Type Info -Text "----------------------" 297 | 298 | if ($Zip) { 299 | Write-au2matorLog -Type Info -Text "Compress the Backup Destination" 300 | 301 | if ($Use7ZIP) { 302 | Write-au2matorLog -Type Info -Text "Use 7ZIP" 303 | if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) { Write-au2matorLog -Type Warning -Text "7Zip not found" } 304 | set-alias sz "$env:ProgramFiles\7-Zip\7z.exe" 305 | #sz a -t7z "$directory\$zipfile" "$directory\$name" 306 | 307 | if ($UseStaging -and $Zip) { 308 | $Zip = $Staging + ("\" + $Backupdir.Replace($Staging, '').Replace('\', '') + ".zip") 309 | sz a -t7z $Zip $Backupdir 310 | 311 | Write-au2matorLog -Type Info -Text "Move Zip to Destination" 312 | Move-Item -Path $Zip -Destination $Destination 313 | 314 | if ($ClearStaging) { 315 | Write-au2matorLog -Type Info -Text "Clear Staging" 316 | Get-ChildItem -Path $Staging -Recurse -Force | remove-item -Confirm:$false -Recurse -force 317 | } 318 | 319 | } 320 | else { 321 | sz a -t7z ($Destination + ("\" + $Backupdir.Replace($Destination, '').Replace('\', '') + ".zip")) $Backupdir 322 | } 323 | 324 | } 325 | else { 326 | Write-au2matorLog -Type Info -Text "Use Powershell Compress-Archive" 327 | Compress-Archive -Path $Backupdir -DestinationPath ($Destination + ("\" + $Backupdir.Replace($Destination, '').Replace('\', '') + ".zip")) -CompressionLevel Optimal -Force 328 | 329 | } 330 | 331 | If ($RemoveBackupDestination) { 332 | Write-au2matorLog -Type Info -Text "$Duration" 333 | 334 | #Remove-Item -Path $BackupDir -Force -Recurse 335 | get-childitem -Path $BackupDir -recurse -Force | remove-item -Confirm:$false -Recurse 336 | get-item -Path $BackupDir | remove-item -Confirm:$false -Recurse 337 | } 338 | } 339 | } 340 | 341 | Write-Host "Press any key to close ..." 342 | 343 | $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") 344 | -------------------------------------------------------------------------------- /BackupScript_v2.ps1: -------------------------------------------------------------------------------- 1 | ######################################################## 2 | # Name: BackupScript_v2.ps1 3 | # Creator: Michael Seidl aka Techguy 4 | # CreationDate: 05.08.2021 5 | # LastModified: 05.08.2021 6 | # Version: 2.1 7 | # Doc: http://www.techguy.at/tag/backupscript/ 8 | # GitHub: https://github.com/Seidlm/PowerShell-Backup-Script 9 | # PSVersion tested: 5 10 | # 11 | # PowerShell Self Service Web Portal at www.au2mator.com/PowerShell 12 | # 13 | # 14 | # Description: Copies the Bakupdirs to the Destination 15 | # You can configure more than one Backupdirs, every Dir 16 | # wil be copied to the Destination. 17 | # Only Change Variables in Variables Section 18 | # Change LoggingLevel to 3 an get more output in Powershell Windows 19 | # 20 | # 21 | ######################################################## 22 | # 23 | # www.techguy.at 24 | # www.facebook.com/TechguyAT 25 | # www.twitter.com/TechguyAT 26 | # michael@techguy.at 27 | # 28 | # 29 | ######################################################## 30 | 31 | 32 | #Variables, only Change here 33 | $Destination = "C:\temp\_Backup" #Copy the Files to this Location 34 | 35 | $Versions = "3" #How many of the last Backups you want to keep 36 | $Backupdirs = "C:\Source1", "C:\Source2" #What Folders you want to backup 37 | $ExcludeDirs = ($env:SystemDrive + "\Users\.*\AppData\Local"), "C:\Program Files (x86)\Common Files\Adobe" #This list of Directories will not be copied 38 | 39 | $logPath = "C:\temp\_Backup" 40 | $LogfileName = "Log" #Log Name 41 | $LoggingLevel = "3" #LoggingLevel only for Output in Powershell Window, 1=smart, 3=Heavy 42 | 43 | $Zip = $false #Zip the Backup Destination 44 | $Use7ZIP = $false #7ZIP Module will be installed https://www.powershellgallery.com/packages/7Zip4Powershell/2.0.0 45 | $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 46 | $StagingPath = "C:\temp\_Staging" 47 | 48 | $RemoveBackupDestination = $true #Remove copied files after Zip, only if $Zip is true 49 | 50 | 51 | #region Functions 52 | 53 | function Write-au2matorLog { 54 | [CmdletBinding()] 55 | param 56 | ( 57 | [ValidateSet('DEBUG', 'INFO', 'WARNING', 'ERROR')] 58 | [string]$Type, 59 | [string]$Text 60 | ) 61 | 62 | # Set logging path 63 | if (!(Test-Path -Path $logPath)) { 64 | try { 65 | $null = New-Item -Path $logPath -ItemType Directory 66 | Write-Verbose ("Path: ""{0}"" was created." -f $logPath) 67 | } 68 | catch { 69 | Write-Verbose ("Path: ""{0}"" couldn't be created." -f $logPath) 70 | } 71 | } 72 | else { 73 | Write-Verbose ("Path: ""{0}"" already exists." -f $logPath) 74 | } 75 | [string]$logFile = '{0}\{1}_{2}.log' -f $logPath, $(Get-Date -Format 'yyyyMMdd'), $LogfileName 76 | $logEntry = '{0}: <{1}> <{2}> {3}' -f $(Get-Date -Format dd.MM.yyyy-HH:mm:ss), $Type, $PID, $Text 77 | 78 | try { Add-Content -Path $logFile -Value $logEntry } 79 | catch { 80 | Start-sleep -Milliseconds 50 81 | Add-Content -Path $logFile -Value $logEntry 82 | } 83 | if ($LoggingLevel -eq "3") { Write-Host $Text } 84 | 85 | 86 | } 87 | 88 | #endregion Functions 89 | 90 | #System Variables, do not change 91 | $PreCheck = $true 92 | $BackUpCheck = $false 93 | $FinalBackupdirs = @() 94 | 95 | 96 | #SCRIPT 97 | ##PRE CHECK 98 | Write-au2matorLog -Type Info -Text "Start the Script" 99 | Write-au2matorLog -Type Info -Text "Create Backup Dirs and Check all Folders an Path if they exist" 100 | 101 | try { 102 | #Create Backup Dir 103 | $BackupDestination = $Destination + "\Backup-" + (Get-Date -format yyyy-MM-dd) + "-" + (Get-Random -Maximum 100000) + "\" 104 | New-Item -Path $BackupDestination -ItemType Directory | Out-Null 105 | Start-sleep -Seconds 5 106 | Write-au2matorLog -Type Info -Text "Create Backupdir $BackupDestination" 107 | 108 | try { 109 | #Ceck all Directories 110 | Write-au2matorLog -Type Info -Text "Check if BackupDirs exist" 111 | foreach ($Dir in $Backupdirs) { 112 | if ((Test-Path $Dir)) { 113 | 114 | Write-au2matorLog -Type INFO -Text "$Dir is fine" 115 | $FinalBackupdirs += $Dir 116 | } 117 | else { 118 | Write-au2matorLog -Type WARNING -Text "$Dir does not exist and was removed from Backup" 119 | } 120 | } 121 | try { 122 | if ($UseStaging) { 123 | if ((Test-Path $StagingPath)) { 124 | 125 | Write-au2matorLog -Type INFO -Text "$StagingPath is fine" 126 | } 127 | else { 128 | Write-au2matorLog -Type ERROR -Text "$StagingPath does not exist" 129 | Write-au2matorLog -Type ERROR -Text $Error 130 | $PreCheck = $false 131 | } 132 | } 133 | } 134 | catch { 135 | Write-au2matorLog -Type ERROR -Text "Failed to Check Staging Dir $StagingPath" 136 | Write-au2matorLog -Type ERROR -Text $Error 137 | $PreCheck = $false 138 | } 139 | } 140 | catch { 141 | Write-au2matorLog -Type ERROR -Text "Failed to Check Backupdir $BackupDestination" 142 | Write-au2matorLog -Type ERROR -Text $Error 143 | $PreCheck = $false 144 | 145 | } 146 | } 147 | catch { 148 | Write-au2matorLog -Type ERROR -Text "Failed to Create Backupdir $BackupDestination" 149 | Write-au2matorLog -Type ERROR -Text $Error 150 | $PreCheck = $false 151 | } 152 | 153 | 154 | ## BACKUP 155 | if ($PreCheck) { 156 | Write-au2matorLog -Type INFO -Text "PreCheck was good, so start with Backup" 157 | 158 | try { 159 | Write-au2matorLog -Type INFO -Text "Calculate Size and check Files" 160 | $BackupDirFiles = @{ } #Hash of BackupDir & Files 161 | $Files = @() 162 | $SumMB = 0 163 | $SumItems = 0 164 | $SumCount = 0 165 | $colItems = 0 166 | $ExcludeString = "" 167 | foreach ($Entry in $ExcludeDirs) { 168 | #Exclude the directory itself 169 | $Temp = "^" + $Entry.Replace("\", "\\").Replace("(", "\(").Replace(")", "\)") + "$" 170 | 171 | #$Temp = $Entry 172 | $ExcludeString += $Temp + "|" 173 | 174 | #Exclude the directory's children 175 | $Temp = "^" + $Entry.Replace("\", "\\").Replace("(", "\(").Replace(")", "\)") + "\\.*" 176 | 177 | #$Temp = $Entry 178 | $ExcludeString += $Temp + "|" 179 | } 180 | $ExcludeString = $ExcludeString.Substring(0, $ExcludeString.Length - 1) 181 | [RegEx]$exclude = $ExcludeString 182 | 183 | foreach ($Backup in $FinalBackupdirs) { 184 | 185 | $Files = Get-ChildItem -LiteralPath $Backup -recurse -Attributes D+!ReparsePoint, D+H+!ReparsePoint -ErrorVariable +errItems -ErrorAction SilentlyContinue | 186 | ForEach-Object -Process { Add-Member -InputObject $_ -NotePropertyName "ParentFullName" -NotePropertyValue ($_.FullName.Substring(0, $_.FullName.LastIndexOf("\" + $_.Name))) -PassThru -ErrorAction SilentlyContinue } | 187 | Where-Object { $_.FullName -notmatch $exclude -and $_.ParentFullName -notmatch $exclude } | 188 | Get-ChildItem -Attributes !D -ErrorVariable +errItems -ErrorAction SilentlyContinue | Where-Object { $_.DirectoryName -notmatch $exclude } 189 | #$BackupDirFiles.Add($Backup, $Files) 190 | 191 | $Files+= Get-ChildItem -LiteralPath $Backup | 192 | ForEach-Object -Process { Add-Member -InputObject $_ -NotePropertyName "ParentFullName" -NotePropertyValue ($_.FullName.Substring(0, $_.FullName.LastIndexOf("\" + $_.Name))) -PassThru -ErrorAction SilentlyContinue } | 193 | Get-ChildItem -Attributes !D -ErrorVariable +errItems -ErrorAction SilentlyContinue 194 | $BackupDirFiles.Add($Backup, $Files) 195 | 196 | 197 | $colItems = ($Files | Measure-Object -property length -sum) 198 | $Items = 0 199 | 200 | $SumMB += $colItems.Sum.ToString() 201 | $SumItems += $colItems.Count 202 | } 203 | 204 | $TotalMB = "{0:N2}" -f ($SumMB / 1MB) + " MB of Files" 205 | Write-au2matorLog -Type INFO -Text "There are $SumItems Files with $TotalMB to copy" 206 | 207 | #Log any errors from above from building the list of files to backup. 208 | [System.Management.Automation.ErrorRecord]$errItem = $null 209 | foreach ($errItem in $errItems) { 210 | Write-au2matorLog -Type WARNING -Text ("Skipping `"" + $errItem.TargetObject + "`" Error: " + $errItem.CategoryInfo) 211 | } 212 | Remove-Variable errItem 213 | Remove-Variable errItems 214 | 215 | try { 216 | Write-au2matorLog -Type INFO -Text "Run Backup" 217 | 218 | 219 | foreach ($Backup in $FinalBackupdirs) { 220 | $Index = $Backup.LastIndexOf("\") 221 | $SplitBackup = $Backup.substring(0, $Index) 222 | $Files = $BackupDirFiles[$Backup] 223 | 224 | foreach ($File in $Files) { 225 | $restpath = $file.fullname.replace($SplitBackup, "") 226 | try { 227 | # Use New-Item to create the destination directory if it doesn't yet exist. Then copy the file. 228 | New-Item -Path (Split-Path -Path $($BackupDestination + $restpath) -Parent) -ItemType "directory" -Force -ErrorAction SilentlyContinue | Out-Null 229 | Copy-Item -LiteralPath $file.fullname $($BackupDestination + $restpath) -Force -ErrorAction SilentlyContinue | Out-Null 230 | Write-au2matorLog -Type Info -Text $("'" + $File.FullName + "' was copied") 231 | } 232 | catch { 233 | $ErrorCount++ 234 | Write-au2matorLog -Type Error -Text $("'" + $File.FullName + "' returned an error and was not copied") 235 | } 236 | $Items += (Get-item -LiteralPath $file.fullname).Length 237 | $Index = [array]::IndexOf($BackupDirs, $Backup) + 1 238 | $Text = "Copy data Location {0} of {1}" -f $Index , $BackupDirs.Count 239 | if ($File.Attributes -ne "Directory") { $count++ } 240 | } 241 | } 242 | $SumCount += $Count 243 | $SumTotalMB = "{0:N2}" -f ($Items / 1MB) + " MB of Files" 244 | Write-au2matorLog -Type Info -Text "----------------------" 245 | Write-au2matorLog -Type Info -Text "Copied $SumCount files with $SumTotalMB" 246 | if ($ErrorCount ) { Write-au2matorLog -Type Info -Text "$ErrorCount Files could not be copied" } 247 | 248 | $BackUpCheck = $true 249 | } 250 | catch { 251 | 252 | Write-au2matorLog -Type ERROR -Text "Failed to Backup" 253 | Write-au2matorLog -Type ERROR -Text $Error 254 | $BackUpCheck = $false 255 | } 256 | } 257 | catch { 258 | Write-au2matorLog -Type ERROR -Text "Failed to Measure Backupdir" 259 | Write-au2matorLog -Type ERROR -Text $Error 260 | $BackUpCheck = $false 261 | } 262 | } 263 | else { 264 | Write-au2matorLog -Type ERROR -Text "PreCheck failed so do not run Backup" 265 | $BackUpCheck = $false 266 | } 267 | 268 | 269 | ## ZIP 270 | if ($BackUpCheck) { 271 | Write-au2matorLog -Type INFO -Text "BAckUpCheck is fine, so lets se if we need to ZIP" 272 | 273 | if ($ZIP) { 274 | Write-au2matorLog -Type INFO -Text "ZIP is on, so lets go" 275 | 276 | if ($Use7ZIP) { 277 | Write-au2matorLog -Type INFO -Text "We should use 7Zip for this" 278 | 279 | try { 280 | Write-au2matorLog -Type INFO -Text "Check for the 7ZIP Module" 281 | if (Get-Module -Name 7Zip4Powershell) { 282 | Write-au2matorLog -Type INFO -Text "7ZIP Module is installed" 283 | } 284 | else { 285 | 286 | Write-au2matorLog -Type INFO -Text "7ZIP Module is not installed, try to install" 287 | Install-Module -Name 7Zip4Powershell -Force 288 | Import-Module 7Zip4Powershell 289 | } 290 | 291 | $Zip = $StagingPath + ("\" + $BackupDestination.Replace($Destination, '').Replace('\', '') + ".zip") 292 | 293 | Write-au2matorLog -Type Info -Text "Compress File" 294 | Compress-7Zip -ArchiveFileName $Zip -Path $BackupDestination 295 | 296 | Write-au2matorLog -Type Info -Text "Move Zip to Destination" 297 | Move-Item -Path $Zip -Destination $Destination 298 | 299 | $ZIPCheck = $true 300 | } 301 | catch { 302 | Write-au2matorLog -Type ERROR -Text "Error on 7ZIP compression" 303 | Write-au2matorLog -Type ERROR -Text $Error 304 | $ZIPCheck = $false 305 | } 306 | } 307 | else { 308 | 309 | } 310 | } 311 | else { 312 | Write-au2matorLog -Type INFO -Text "No Zip, so go ahead" 313 | } 314 | 315 | 316 | } 317 | else { 318 | Write-au2matorLog -Type ERROR -Text "BAckUpCheck failed so do not try to ZIP" 319 | } 320 | 321 | 322 | 323 | 324 | 325 | ##CLEANUP BACKUP 326 | if ($Zip -and $RemoveBackupDestination -and $ZIPCheck) 327 | { 328 | try { 329 | Write-au2matorLog -Type INFO -Text "Lets remove Backup Dir after ZIP" 330 | #Remove-Item -Path $BackupDir -Force -Recurse 331 | get-childitem -Path $BackupDestination -recurse -Force | remove-item -Confirm:$false -ErrorAction SilentlyContinue -Recurse 332 | get-item -Path $BackupDestination | remove-item -Confirm:$false -ErrorAction SilentlyContinue -Recurse | Out-Null 333 | 334 | } 335 | catch { 336 | Write-au2matorLog -Type ERROR -Text "Error to Remove Backup Dir: $BackupDestination" 337 | Write-au2matorLog -Type ERROR -Text $Error 338 | 339 | } 340 | } 341 | 342 | 343 | ##CLEANUP VERSION 344 | Write-au2matorLog -Type Info -Text "Cleanup Backup Dir" 345 | 346 | $Count = (Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Directory" }).count 347 | if ($count -gt $Versions) { 348 | Write-au2matorLog -Type Info -Text "Found $count Backups" 349 | $Folder = Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Directory" } | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1 350 | 351 | Write-au2matorLog -Type Info -Text "Remove Dir: $Folder" 352 | 353 | $Folder.FullName | Remove-Item -Recurse -Force 354 | } 355 | 356 | 357 | $CountZip = (Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Archive" -and $_.Extension -eq ".zip" }).count 358 | Write-au2matorLog -Type Info -Text "Check if there are more than $Versions Zip in the Backupdir" 359 | 360 | if ($CountZip -gt $Versions) { 361 | 362 | $Zip = Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Archive" -and $_.Extension -eq ".zip" } | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1 363 | 364 | Write-au2matorLog -Type Info -Text "Remove Zip: $Zip" 365 | 366 | $Zip.FullName | Remove-Item -Recurse -Force 367 | 368 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Michael Seidl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This Script is not longer maintained. Feel free to adapt it as you need it. 2 | 3 | # Name: BackupScript.ps1 4 | Creator: Michael Seidl aka Techguy 5 | CreationDate: 05.08.2021 6 | LastModified: 05.11.2021 7 | Version: 2.1 8 | Doc: http://www.techguy.at/tag/backupscript/ 9 | GitHub: https://github.com/Seidlm/PowerShell-Backup-Script 10 | PSVersion tested: 5 11 | 12 | 13 | # PowerShell Self Service Web Portal at https://www.au2mator.com/PowerShell 14 | 15 | 16 | # Description: 17 | Copies the Bakupdirs to the Destination 18 | You can configure more than one Backupdirs, every Dir 19 | wil be copied to the Destination. 20 | Only Change Variables in Variables Section 21 | Change LoggingLevel to 3 an get more output in Powershell Windows 22 | 23 | 24 | # Version 2.1 (05.11.2021) 25 | FIX: Root Files are now inlcuded in Backup 26 | 27 | # Version 2.0 (05.08.2021) 28 | Published v2 Verion 29 | 30 | # Version 1.5 (31.03.2020) 31 | FIX: Github: Symbolic Links are now supported 32 | FIX: Github: Sibling Folders 33 | FIX: Github: Backup Duration 34 | NEW: Rewrite Loggign Function 35 | DIF: Some Code write ups 36 | # Version 1.4 37 | NEW: 7ZIP Support 38 | FIX: Ordering at old Backup deletion 39 | FIX: Exclude Dir is now working 40 | NEW: Staging folder for ZIP 41 | # Version 1.3 42 | NEW: Send Mail Function 43 | NEW: Backup Destination will be zipped 44 | NEW: Exclude Dir 45 | FIX: Logging Level 46 | FIX: Delete old Folder by CreationTime 47 | 48 | # Version 1.2 49 | FIX: Delete last Backup dirs, changed to support older PS Version 50 | FIX: Fixed the Count in the Statusbar 51 | FIX: Fixed Location Count in Statusbar 52 | 53 | # Version 1.1 54 | CHANGE: Enhanced the Logging to a Textfile and write output, copy Log file to Backupdir 55 | FIX: Renamed some Variables an have done some cosmetic changes 56 | CHANGE: Define the Log Name in Variables 57 | 58 | # Version 1.0 - RTM 59 | 60 | # Notes 61 | www.techguy.at 62 | www.facebook.com/TechguyAT 63 | www.twitter.com/TechguyAT 64 | michael@techguy.at 65 | --------------------------------------------------------------------------------