write-host "Hyper-V Backup by GT (version 0.1) - Starting Backup" ##################################################################################### function import-ini ($file) { $ini = @{} switch -regex -file $file { "^\[(.+)\]$" { $section = $matches[1] $ini[$section] = @{} } #"(.+)=(.+)" "(.+?)\s*=\s*(.*)" # Key { $name,$value = $matches[1..2] $ini[$section][$name] = $value } } $ini } function FileVersioning ([string]$DIR, [string]$VERSIONS, [string]$FILENAME){ #loop versions backward until null #example call FileVersioning($DIR,$VERSIONS,$FILENAME) # FileVersioning("c:\test","3","FullBackup_T-?") ?will be the version $VER=[int]$VERSIONS switch ($VER) { 0 { #just delete version 0 $curFilename=$FILENAME -replace "VERSIONNO","0" $fullFile=$DIR+"\"+$curFilename Write-Host "deleting file $fullFile" Remove-Item $fullFile } default { #working with 1 ore more versions #loop versions $i=$VER while ($i -ne 0) { # count . . 4 3 2 1 # delete curr ver if exist # move ver -1 to curr $curFilename=$FILENAME -replace "VERSIONNO", $i $fullFile=$DIR+"\"+$curFilename If (Test-Path $fullFile){ Write-Host "deleting file $fullFile" Remove-Item $fullFile} #delete current $k=$i-1 $befFilename=$FILENAME -replace "VERSIONNO", $k $beffullFile=$DIR+"\"+$befFilename #write-host "beffullFile:$beffullFile" If (Test-Path $beffullFile){ Write-Host " moving $beffullFile to $fullFile" mv $beffullFile $fullFile } #move before to current $i-- # count one down }#close while }#close default }#close switch }# close function FileVersioning ############################################################################## # Hyper-V Backup Script # Version 0.01 Initial Release # ############################################################################## # Open for Test # # # # Open ToDo's # # - Check VM Name if exits and remove if not existing // to many to do ... :-) # - check if mesure objekt is available // beautiful task -> later # ############################################################################## # Script Directory $ScriptDir = $PSScriptRoot $ScriptName = $MyInvocation.MyCommand.name.basename # Logfile Directory $ScriptLogDir = $ScriptDir + "\LOG" $hname=$env:computername # $inivars = import-ini $ScriptDir\HV-Backup-GT.ini # Set-ExecutionPolicy unrestricted ############################################################################## # Definition of Variables ############################################################################## # Export definition $Export2Path = $inivars["EXPORT"]["Path"] $Backup2Path = $inivars["BACKUP"]["Path"] # Duplicate to path using net use command $DoDuplicate = $inivars["DUPLICATE"]["Doit"] # TRUE/FALSE $DoDuplicateUsingSmbMount = $inivars["DUPLICATE"]["UsingSmbMount"] # TRUE/FALSE $Duplicate2Path = $inivars["DUPLICATE"]["Path"] $Duplicate2PathUsername = $inivars["DUPLICATE"]["Username"] $Duplicate2PathPassword = $inivars["DUPLICATE"]["Password"] $DoBackup = $inivars["BACKUP"]["Doit"] # TRUE/FALSE $DoBackupUsingSmbMount = $inivars["BACKUP"]["UsingSmbMount"] # TRUE/FALSE $Backup2PathUsername = $inivars["BACKUP"]["Username"] $Backup2PathPassword = $inivars["BACKUP"]["Password"] $KeepBackupVersions = $inivars["BACKUP"]["VERSIONS"] $KeepDuplicateVersions = $inivars["DUPLICATE"]["VERSIONS"] if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {throw "$env:ProgramFiles\7-Zip\7z.exe needed"} $7zipExec = "$env:ProgramFiles\7-Zip\7z.exe" $7zipArgumentsFirst = $inivars["7ZipOptions"]["ArgumentsFirst"] $7zipFileEnding = $inivars["7ZipOptions"]["FileEnding"] $7zipArgumentsLast = $inivars["7ZipOptions"]["ArgumentsLast"] ############################################################################## # Send E-Mail ############################################################################## $SendMail = $inivars["MAIL"]["Doit"] $smtp = New-Object System.Net.Mail.SmtpClient $smtp.Host = $inivars["MAIL"]["SMTPHost"] #DNS oder IP $MailMessage = New-Object system.net.mail.mailmessage $MailMessage.From = $inivars["MAIL"]["From"] $MailMessage.To.Add($inivars["MAIL"]["To"]) $MailMessage.Subject = "Hyper-V-Backup on $hname (PowerShell Email)" $SmtpUser = New-Object System.Net.NetworkCredential $SmtpUser.UserName = $inivars["MAIL"]["AuthUser"] $SmtpUser.Password = $inivars["MAIL"]["AuthPass"] $a = get-date -f yyy-MM-dd___HH-mm-ss $ScriptStartTime = $a $attfile = $ScriptLogDir+"\"+$a+".html" #$VMs = get-vm | select vmname #$VMs = @("Virtual XP") $VMs = $inivars["GLOBAL"]["VM"] $VMs = $VMs.Split(",") $WaitBeforeClose = $inivars["GLOBAL"]["WaitBeforeClose"] # will wait with "press any key to continue if $true" $LocalOSVersion = (Get-WmiObject -class Win32_OperatingSystem).Caption $DeleteLogFiles=$inivars["LOG"]["Doit"] $DeleteLogFilesDays=$inivars["LOG"]["DeleteOlderThan"] #X Days $DeleteLogFilesLimit = (Get-Date).AddDays($DeleteLogFilesDays) ############################################################################################################################################################ # DO NOT EDIT AFTER THIS LINE ############################################################################################################################################################ write-host "Starting with local Hyper-V Backup on Host: $hname" (Get-Date).ToString() write-host #check if VM exist for ($i=0; $i -lt $VMs.length; $i++) { $VM = $VMs[$i] $VMName = Get-VM -name $VM if (!$VMname) { Write-Host "No VM named $VM exists" }else{ } }# close for loop for VM check # Report VM's to backup Write-Host "Backing up the following VMs:" echo $VMs write-host # Create Log Folder if not exist if(!(Test-Path -Path $ScriptLogDir )){ Write-Host "Creating $ScriptLogDir" New-Item -ItemType directory -Path $ScriptLogDir } # Delete old Logfiles if needed and selected if($DeleteLogFiles -eq "TRUE"){ $days = $DeleteLogFilesDays Write-Host "Deleting Logfiles older than $days" Get-ChildItem -Path $ScriptLogDir -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $DeleteLogFilesLimit } | Remove-Item -Force } # mount remote directory if necessary if($DoDuplicateUsingSmbMount -eq "TRUE"){ Write-Host "Mounting Remote directory ... " net use $Duplicate2Path /user:$Duplicate2PathUsername $Duplicate2PathPassword Write-Host "done" } # mount remote directory if necessary if($DoBackupUsingSmbMount -eq "TRUE"){ Write-Host "Mounting Remote directory ... " net use $Backup2Path /user:$Backup2PathUsername $Backup2PathPassword Write-Host "done" } # necessary for windows 2008 server - they need to enable additional hyper-v commands if ($LocalOSVersion -like "Microsoft Windows Server 2008 R2*"){ Write-Host "Loading Hyper-V Addons for 2008 R2" Import-Module 'C:\Program Files\modules\Hyperv\HyperV.psd1' } # loop for every VM - clear old file Version's for ($i=0; $i -lt $VMs.length; $i++) { $VM = $VMs[$i] # VMname $FILENAME=$VM+".T-VERSIONNO.$7zipFileEnding" FileVersioning $Backup2Path $KeepBackupVersions $FILENAME FileVersioning $Duplicate2Path $KeepDuplicateVersions $FILENAME }# close loop for every VM # Save VM's and Export for ($i=0; $i -lt $VMs.length; $i++) { $VM = $VMs[$i] Write-Host "Checking Export Dir for existing Export folder" $strFileName = "$Export2Path\$VM" If (Test-Path $strFileName){ Remove-Item $strFileName -Recurse Write-Host "found old folder .. deleting $strFileName" }Else{ Write-Host "can't find file $strFileName" } Write-Host "Exporting $VM ... " -NoNewLine if ($LocalOSVersion -like "Microsoft Windows Server 2008 R2*") { save-vm $VM -force -wait Export-VM $VM –Path $Export2Path -wait -CopyState start-vm $VM -wait } if ($LocalOSVersion -like "Microsoft Windows 8*") { save-vm -Name $VM export-vm -Name $VM -Path $Export2Path start-vm -Name $VM } if ($LocalOSVersion -like "Microsoft Windows Server 2012 R2*") { Save-VM -Name $VM export-vm -Name $VM -Path $Export2Path start-vm -Name $VM } Write-Host "done" } Write-Host # write status message after export and send mail if($SendMail -eq "TRUE") { #set message content $MailMessage.Body = " " $MailMessage.Body +="Export finished" #write log for attachement & $ScriptDir\get-bufferhtml.ps1 -all > $attfile #send mail Write-Host -NoNewLine "Sending E-Mail ..... " $attatchment = New-Object System.Net.Mail.Attachment($attfile) $MailMessage.IsBodyHtml = $true $MailMessage.Attachments.Add($attatchment) #Auth $smtp.Credentials = $SmtpUser $smtp.Send($MailMessage) Write-Host "done" } # ZIP, COPY & Move Jobs $HTMLBody =' ' $HTMLBody +="

Hyper-V-Backup Report for $hname

" $HTMLBody +="Backup Start Time: $ScriptStartTime
" $HTMLBody +='' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' # ZIP the export and remove export dir for ($i=0; $i -lt $VMs.length; $i++) { $VM = $VMs[$i] Write-Host "zipping $VM ... " -NoNewLine # for standard zip compression use this #$7zipArgumentsFirst = "a -mmt=ON -mx=0" #Start-Process $7zipExec -ArgumentList "$7zipArgumentsFirst $Backup2Path\$VM.T-0.zip $Export2Path\$VM $7zipArgumentsLast" -Wait -NoNewWindow # optimized with lzma2 compression Start-Process $7zipExec -ArgumentList "$7zipArgumentsFirst $Backup2Path\$VM.T-0.$7zipFileEnding $Export2Path\$VM $7zipArgumentsLast" -Wait -NoNewWindow # 7z a -t7z -m0=lzma2 -mx=9 -ms=on file.7z file.img #$7zipArgumentsFirst = "a -t7z -m0=lzma2 -mx=9 -ms=on" Write-Host "Report for VM: $VM" $colItems = (Get-ChildItem $Export2Path\$VM -recurse | Measure-Object -property length -sum) $DirSize = $colItems.sum/1GB $DirSize = "{0:N2}" -f $DirSize Write-Host " Export Size: $DirSize GB" $file = Get-Item $Backup2Path\$VM.T-0.$7zipFileEnding $FileSize = $file.length/1GB $FileSize = "{0:N2}" -f $FileSize Write-Host " Packed Size: $FileSize GB" $compressratio = (1-($FileSize/$DirSize))*100 $compressratio = "{0:N2}" -f $compressratio Write-Host " Compress ratio: $compressratio %" if($DoDuplicate -eq "TRUE"){ Write-Host "Duplicating to destination: $Duplicate2Path" cp $Backup2Path\$VM.T-0.$7zipFileEnding $Duplicate2Path\$VM.T-0.$7zipFileEnding -Recurse -Force } Write-Host "removing latest VM-Export (VM:$VM) export from: $Export2Path" $strFileName = "$Export2Path\$VM" If (Test-Path $strFileName){ Remove-Item $strFileName -Recurse Write-Host "done for $strFileName" }Else{ Write-Host "can't find file $strFileName" } # HTML Reporting $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' } $HTMLBody +='
VM NameExport sizePacked sizesaved
'+$VM+''+$DirSize+''+$FileSize+''+$compressratio+' %
' # Reporting Backup Overview $HTMLBody +='

Hyper-V-Backup Overview for all Archive versions on Backup path' $HTMLBody +='' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' for ($i=0; $i -lt $VMs.length; $i++) { for ($k=0; $k -le $KeepBackupVersions; $k++) { $VM = $VMs[$i] $Version = $k $CurFile = $Backup2Path+'\'+$VM+'.T-'+$k+".$7zipFileEnding" Write-Host "Reporting $CurFile" #VM Name :: Create Date :: Size $FileDate = (Get-ChildItem $CurFile).CreationTime $file = Get-Item $CurFile $FileSize = $file.length/1GB $FileSize = "{0:N2}" -f $FileSize $HTMLBody +=' ' $HTMLBody +=' " $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' } } $HTMLBody +='
VM NameCreate Datesize
'+$VM+'.T-'+$k+".$7zipFileEnding'+$FileDate+''+$FileSize+' GB
' # Reporting Duplicate Overview if($DoDuplicate -eq "TRUE"){ $HTMLBody +='

Hyper-V-Backup Overview for all Archive versions on Duplicate path' $HTMLBody +='' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' for ($i=0; $i -lt $VMs.length; $i++) { for ($k=0; $k -le $KeepDuplicateVersions; $k++) { $VM = $VMs[$i] $Version = $k $CurFile = $Duplicate2Path+'\'+$VM+'.T-'+$k+".$7zipFileEnding" Write-Host "Reporting $CurFile" #VM Name :: Create Date :: Size $FileDate = (Get-ChildItem $CurFile).CreationTime $file = Get-Item $CurFile $FileSize = $file.length/1GB $FileSize = "{0:N2}" -f $FileSize $HTMLBody +=' ' $HTMLBody +=' " $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' } } $HTMLBody +='
VM NameCreate Datesize
'+$VM+'.T-'+$k+".$7zipFileEnding'+$FileDate+''+$FileSize+' GB
' # Compare Report $HTMLBody +='

Hyper-V-Backup Compare for all versions' $HTMLBody +='' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' for ($i=0; $i -lt $VMs.length; $i++) { for ($k=0; $k -le $KeepVersions; $k++) { $VM = $VMs[$i] $Version = $k $ArchFile = $Backup2Path+'\'+$VM+'.T-'+$k+".$7zipFileEnding" $DuplFile = $Duplicate2Path+'\'+$VM+'.T-'+$k+".$7zipFileEnding" Write-Host "Reporting $File" #Check Filesize of one to the nother $AFile = Get-Item $ArchFile $AFile = $AFile.length $BFile = Get-Item $DuplFile $BFile = $BFile.length if ($AFile -eq $Bfile){ $CheckResult = "OK" }else{ $CheckResult = "Error in Size" } $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' $HTMLBody +=' ' } } # schließe schleife für compare report $HTMLBody +='
VM ArchiveVM DuplicateCheck Result
'+$ArchFile+''+$DuplFile+''+$CheckResult+'
' } # schließe if doduplicate = true # Write logfile from this Console to Logfile (File in Mail) $a = get-date -f yyy-MM-dd___HH-mm-ss $attfile = $ScriptLogDir+"\"+$a+".html" & $ScriptDir\get-bufferhtml.ps1 -all > $attfile $HTMLHead = " " $HTMLHead += "" $HTMLHead += "" $HTMLHead += "" $HTMLHead += " " $HTMLHead += " " $a = get-date -f yyy-MM-dd___HH-mm-ss $ScriptEndTime = $a $HTMLEnd ="Backup End Time: $ScriptEndTime
" $HTMLEnd += "" #reporting all VM's on system Get-VM if($SendMail -eq "TRUE") { $MailMessage.Body = " " $MailMessage.Body +=$HTMLHead $MailMessage.Body +=$HTMLBody $MailMessage.Body +=$HTMLEnd Write-Host -NoNewLine "Sending E-Mail ..... " $attatchment = New-Object System.Net.Mail.Attachment($attfile) $MailMessage.IsBodyHtml = $true $MailMessage.Attachments.Add($attatchment) #Auth $smtp.Credentials = $SmtpUser $smtp.Send($MailMessage) Write-Host "done" } if($WaitBeforeClose -eq "TRUE"){ Write-Host "Press any key to continue ..." $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") }