PowerShell BackupScript Version 2.0 released

That Script was published in 2014, with minimal skills related to Powershell. You, the Community, loved it through.

Via Github, Mail, Facebook, and Twitter, I received a lot of feedback and Bug Reports 🙂

So today, I published a complete new Release, 2.0! With a lot of fixed Bugs, nearly all Functions the old one had and, in my opinion, better Code.

And of course, with a lot of help from the Community.

The Script

########################################################
# Name: BackupScript_v2.ps1                              
# Creator: Michael Seidl aka Techguy                    
# CreationDate: 05.08.2021                              
# LastModified: 05.08.2021                               
# Version: 2.0
# Doc: http://www.techguy.at/tag/backupscript/
# GitHub: https://github.com/Seidlm/PowerShell-Backup-Script
# PSVersion tested: 5
#
# PowerShell Self Service Web Portal at www.au2mator.com/PowerShell
#
#
# Description: Copies the Bakupdirs to the Destination
# You can configure more than one Backupdirs, every Dir
# wil be copied to the Destination.
# Only Change Variables in Variables Section
# Change LoggingLevel to 3 an get more output in Powershell Windows
# 
#
########################################################
#
# www.techguy.at                                        
# www.facebook.com/TechguyAT                            
# www.twitter.com/TechguyAT                             
# michael@techguy.at 
#
#
########################################################






#Variables, only Change here
$Destination = "C:\temp\_Backup" #Copy the Files to this Location

$Versions = "3" #How many of the last Backups you want to keep
$Backupdirs = "C:\Users\seimi\Documents", "C:\Program Files (x86)\Common Files" #What Folders you want to backup
$ExcludeDirs = ($env:SystemDrive + "\Users\.*\AppData\Local"), "C:\Program Files (x86)\Common Files\Adobe" #This list of Directories will not be copied

$logPath = "C:\temp\_Backup"
$LogfileName = "Log" #Log Name
$LoggingLevel = "3" #LoggingLevel only for Output in Powershell Window, 1=smart, 3=Heavy

$Zip = $true #Zip the Backup Destination
$Use7ZIP = $true #7ZIP Module will be installed https://www.powershellgallery.com/packages/7Zip4Powershell/2.0.0
$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
$StagingPath = "C:\temp\_Staging"

$RemoveBackupDestination = $true #Remove copied files after Zip, only if $Zip is true





#region Functions

function Write-au2matorLog {
    [CmdletBinding()]
    param
    (
        [ValidateSet('DEBUG', 'INFO', 'WARNING', 'ERROR')]
        [string]$Type,
        [string]$Text
    )
       
    # Set logging path
    if (!(Test-Path -Path $logPath)) {
        try {
            $null = New-Item -Path $logPath -ItemType Directory
            Write-Verbose ("Path: ""{0}"" was created." -f $logPath)
        }
        catch {
            Write-Verbose ("Path: ""{0}"" couldn't be created." -f $logPath)
        }
    }
    else {
        Write-Verbose ("Path: ""{0}"" already exists." -f $logPath)
    }
    [string]$logFile = '{0}\{1}_{2}.log' -f $logPath, $(Get-Date -Format 'yyyyMMdd'), $LogfileName
    $logEntry = '{0}: <{1}> <{2}> {3}' -f $(Get-Date -Format dd.MM.yyyy-HH:mm:ss), $Type, $PID, $Text
    
    try { Add-Content -Path $logFile -Value $logEntry }
    catch {
        Start-sleep -Milliseconds 50
        Add-Content -Path $logFile -Value $logEntry
    }
    if ($LoggingLevel -eq "3") { Write-Host $Text }
    
    
}

#endregion Functions

#System Variables, do not change
$PreCheck = $true
$BackUpCheck = $false
$FinalBackupdirs = @()



#SCRIPT
##PRE CHECK
Write-au2matorLog -Type Info -Text "Start the Script"
Write-au2matorLog -Type Info -Text "Create Backup Dirs and Check all Folders an Path if they exist"

try {
    #Create Backup Dir
    $BackupDestination = $Destination + "\Backup-" + (Get-Date -format yyyy-MM-dd) + "-" + (Get-Random -Maximum 100000) + "\"
    New-Item -Path $BackupDestination -ItemType Directory | Out-Null
    Start-sleep -Seconds 5
    Write-au2matorLog -Type Info -Text "Create Backupdir $BackupDestination"

    try {
        #Ceck all Directories
        Write-au2matorLog -Type Info -Text "Check if BackupDirs exist"
        foreach ($Dir in $Backupdirs) {
            if ((Test-Path $Dir)) {
                
                Write-au2matorLog -Type INFO -Text "$Dir is fine"
                $FinalBackupdirs += $Dir
            }
            else {
                Write-au2matorLog -Type WARNING -Text "$Dir does not exist and was removed from Backup"
            }
        }
        try {
            if ($UseStaging) {
                if ((Test-Path $StagingPath)) {
                
                    Write-au2matorLog -Type INFO -Text "$StagingPath is fine"
                }
                else {
                    Write-au2matorLog -Type ERROR -Text "$StagingPath does not exist"
                    Write-au2matorLog -Type ERROR -Text $Error
                    $PreCheck = $false
                }
            }
        }
        catch {
            Write-au2matorLog -Type ERROR -Text "Failed to Check Staging Dir $StagingPath"
            Write-au2matorLog -Type ERROR -Text $Error
            $PreCheck = $false
        }
    }
    catch {
        Write-au2matorLog -Type ERROR -Text "Failed to Check Backupdir $BackupDestination"
        Write-au2matorLog -Type ERROR -Text $Error
        $PreCheck = $false
        
    }
}
catch {
    Write-au2matorLog -Type ERROR -Text "Failed to Create Backupdir $BackupDestination"
    Write-au2matorLog -Type ERROR -Text $Error
    $PreCheck = $false

}




## BACKUP
if ($PreCheck) {
    Write-au2matorLog -Type INFO -Text "PreCheck was good, so start with Backup"

    try {
        Write-au2matorLog -Type INFO -Text "Calculate Size and check Files"
        $BackupDirFiles = @{ } #Hash of BackupDir & Files
        $Files = @()
        $SumMB = 0
        $SumItems = 0
        $SumCount = 0
        $colItems = 0
        $ExcludeString = ""
        foreach ($Entry in $ExcludeDirs) {
            #Exclude the directory itself
            $Temp = "^" + $Entry.Replace("\", "\\").Replace("(", "\(").Replace(")", "\)") + "$"

            #$Temp = $Entry
            $ExcludeString += $Temp + "|"

            #Exclude the directory's children
            $Temp = "^" + $Entry.Replace("\", "\\").Replace("(", "\(").Replace(")", "\)") + "\\.*"

            #$Temp = $Entry
            $ExcludeString += $Temp + "|"
        }
        $ExcludeString = $ExcludeString.Substring(0, $ExcludeString.Length - 1)
        [RegEx]$exclude = $ExcludeString
        
        foreach ($Backup in $FinalBackupdirs) {

            $Files = Get-ChildItem -LiteralPath $Backup -recurse -Attributes D+!ReparsePoint, D+H+!ReparsePoint -ErrorVariable +errItems -ErrorAction SilentlyContinue | 
            ForEach-Object -Process { Add-Member -InputObject $_ -NotePropertyName "ParentFullName" -NotePropertyValue ($_.FullName.Substring(0, $_.FullName.LastIndexOf("\" + $_.Name))) -PassThru -ErrorAction SilentlyContinue } |
            Where-Object { $_.FullName -notmatch $exclude -and $_.ParentFullName -notmatch $exclude } |
            Get-ChildItem -Attributes !D -ErrorVariable +errItems -ErrorAction SilentlyContinue | Where-Object { $_.DirectoryName -notmatch $exclude }
            $BackupDirFiles.Add($Backup, $Files)
    
            $colItems = ($Files | Measure-Object -property length -sum) 
            $Items = 0
            
            $SumMB += $colItems.Sum.ToString()
            $SumItems += $colItems.Count
        }
    
        $TotalMB = "{0:N2}" -f ($SumMB / 1MB) + " MB of Files"
        Write-au2matorLog -Type INFO -Text "There are $SumItems Files with  $TotalMB to copy"
    
        #Log any errors from above from building the list of files to backup.
        [System.Management.Automation.ErrorRecord]$errItem = $null
        foreach ($errItem in $errItems) {
            Write-au2matorLog -Type WARNING -Text ("Skipping `"" + $errItem.TargetObject + "`" Error: " + $errItem.CategoryInfo)
        }
        Remove-Variable errItem
        Remove-Variable errItems

        try {
            Write-au2matorLog -Type INFO -Text "Run Backup"


            foreach ($Backup in $FinalBackupdirs) {
                $Index = $Backup.LastIndexOf("\")
                $SplitBackup = $Backup.substring(0, $Index)
                $Files = $BackupDirFiles[$Backup]
        
                foreach ($File in $Files) {
                    $restpath = $file.fullname.replace($SplitBackup, "")
                    try {
                        # Use New-Item to create the destination directory if it doesn't yet exist. Then copy the file.
                        New-Item -Path (Split-Path -Path $($BackupDestination + $restpath) -Parent) -ItemType "directory" -Force -ErrorAction SilentlyContinue | Out-Null
                        Copy-Item -LiteralPath $file.fullname $($BackupDestination + $restpath) -Force -ErrorAction SilentlyContinue | Out-Null
                        Write-au2matorLog -Type Info -Text $("'" + $File.FullName + "' was copied")
                    }
                    catch {
                        $ErrorCount++
                        Write-au2matorLog -Type Error -Text $("'" + $File.FullName + "' returned an error and was not copied")
                    }
                    $Items += (Get-item -LiteralPath $file.fullname).Length
                    $Index = [array]::IndexOf($BackupDirs, $Backup) + 1
                    $Text = "Copy data Location {0} of {1}" -f $Index , $BackupDirs.Count
                    if ($File.Attributes -ne "Directory") { $count++ }
                }
            }
            $SumCount += $Count
            $SumTotalMB = "{0:N2}" -f ($Items / 1MB) + " MB of Files"
            Write-au2matorLog -Type Info -Text "----------------------"
            Write-au2matorLog -Type Info -Text "Copied $SumCount files with $SumTotalMB"
            if ($ErrorCount ) { Write-au2matorLog -Type Info -Text "$ErrorCount Files could not be copied" }

            $BackUpCheck = $true
        }
        catch {
            
            Write-au2matorLog -Type ERROR -Text "Failed to Backup"
            Write-au2matorLog -Type ERROR -Text $Error
            $BackUpCheck = $false


        }
    }
    catch {
        Write-au2matorLog -Type ERROR -Text "Failed to Measure Backupdir"
        Write-au2matorLog -Type ERROR -Text $Error
        $BackUpCheck = $false
    



    }






}
else {
    Write-au2matorLog -Type ERROR -Text "PreCheck failed so do not run Backup"
    $BackUpCheck = $false
}





## ZIP
if ($BackUpCheck) {
    Write-au2matorLog -Type INFO -Text "BAckUpCheck is fine, so lets se if we need to ZIP"

    
    if ($ZIP) {
        Write-au2matorLog -Type INFO -Text "ZIP is on, so lets go"



        if ($Use7ZIP) {
            Write-au2matorLog -Type INFO -Text "We should use 7Zip for this"
        
            try {
                Write-au2matorLog -Type INFO -Text "Check for the 7ZIP Module"
                if (Get-Module -Name 7Zip4Powershell) {
                    Write-au2matorLog -Type INFO -Text "7ZIP Module is installed"

                }
                else {
                
                    Write-au2matorLog -Type INFO -Text "7ZIP Module is not installed, try to install"
                    Install-Module -Name 7Zip4Powershell -Force
                    Import-Module 7Zip4Powershell

                    


                }

                $Zip = $StagingPath + ("\" + $BackupDestination.Replace($Destination, '').Replace('\', '') + ".zip")
                
                Write-au2matorLog -Type Info -Text "Compress File"
                Compress-7Zip -ArchiveFileName $Zip -Path $BackupDestination
                            
                Write-au2matorLog -Type Info -Text "Move Zip to Destination"
                Move-Item -Path $Zip -Destination $Destination



                $ZIPCheck = $true
            }
            catch {
                Write-au2matorLog -Type ERROR -Text "Error on 7ZIP compression"
                Write-au2matorLog -Type ERROR -Text $Error
                $ZIPCheck = $false
            }


        }
        else {
        
        }
    }
    else {
        Write-au2matorLog -Type INFO -Text "No Zip, so go ahead"
    }


}
else {
    Write-au2matorLog -Type ERROR -Text "BAckUpCheck failed so do not try to ZIP"
}





##CLEANUP BACKUP
if ($Zip -and $RemoveBackupDestination -and $ZIPCheck)
{
    try {
        Write-au2matorLog -Type INFO -Text "Lets remove Backup Dir after ZIP"
        #Remove-Item -Path $BackupDir -Force -Recurse 
        get-childitem -Path $BackupDestination -recurse -Force | remove-item -Confirm:$false -ErrorAction SilentlyContinue -Recurse 
        get-item -Path $BackupDestination | remove-item -Confirm:$false  -ErrorAction SilentlyContinue -Recurse | Out-Null

    }
    catch {
        Write-au2matorLog -Type ERROR -Text "Error to Remove Backup Dir: $BackupDestination"
        Write-au2matorLog -Type ERROR -Text $Error
        
    }
}


##CLEANUP VERSION
Write-au2matorLog -Type Info -Text "Cleanup Backup Dir"

$Count = (Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Directory" }).count
if ($count -gt $Versions) {
    Write-au2matorLog -Type Info -Text "Found $count Backups"
    $Folder = Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Directory" } | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1

    Write-au2matorLog -Type Info -Text "Remove Dir: $Folder"
    
    $Folder.FullName | Remove-Item -Recurse -Force 
}


$CountZip = (Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Archive" -and $_.Extension -eq ".zip" }).count
Write-au2matorLog -Type Info -Text "Check if there are more than $Versions Zip in the Backupdir"

if ($CountZip -gt $Versions) {

    $Zip = Get-ChildItem $Destination | Where-Object { $_.Attributes -eq "Archive" -and $_.Extension -eq ".zip" } | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1

    Write-au2matorLog -Type Info -Text "Remove Zip: $Zip"
    
    $Zip.FullName | Remove-Item -Recurse -Force 

}

GitHub Repo

Make sure you get the latest version from GitHub.

Seidlm/PowerShell-Backup-Script: PowerShell Backup Script (github.com)

Michael Seidl aka Techguy
au2mate everything

4 thoughts on “PowerShell BackupScript Version 2.0 released”

  1. Hallo,

    ist es möglich, Dateien außerhalb von Unterordner zu (mit)kopieren?

    Vielen Dank 🙂

  2. Hallo

    Vielen Dank für den Script. Ich finde das ganze echt toll. Vor allem mit den möglichen Backup-Versionen.
    Wenn ich nun einen zu speichernden Pfad definiere, dann werden einzig die Unterordner (und die darin enthaltenen Dateien) gespeichert. Jedoch nicht die im Pfad abgespeicherten Dateien.
    Beispiel:

    $BackupDirs = “C:\Temp\OrdnerSave”

    Nun werden die im OrderSave befindlichen UnterOrdner abespeichert. Aber die Files, die direkt im OrdnerSave liegen werden nicht in den $Destination-Pfad kopiert.

Leave a Comment

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

*

%d bloggers like this: