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 +=' VM Name | '
$HTMLBody +=' Export size | '
$HTMLBody +=' Packed size | '
$HTMLBody +=' saved | '
$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 +=' '+$VM+' | '
$HTMLBody +=' '+$DirSize+' | '
$HTMLBody +=' '+$FileSize+' | '
$HTMLBody +=' '+$compressratio+' % | '
$HTMLBody +='
'
}
$HTMLBody +='
'
# Reporting Backup Overview
$HTMLBody +='
Hyper-V-Backup Overview for all Archive versions on Backup path'
$HTMLBody +=''
$HTMLBody +=' '
$HTMLBody +=' VM Name | '
$HTMLBody +=' Create Date | '
$HTMLBody +=' size | '
$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 +=' '+$VM+'.T-'+$k+".$7zipFileEnding | "
$HTMLBody +=' '+$FileDate+' | '
$HTMLBody +=' '+$FileSize+' GB | '
$HTMLBody +='
'
}
}
$HTMLBody +='
'
# Reporting Duplicate Overview
if($DoDuplicate -eq "TRUE"){
$HTMLBody +='
Hyper-V-Backup Overview for all Archive versions on Duplicate path'
$HTMLBody +=''
$HTMLBody +=' '
$HTMLBody +=' VM Name | '
$HTMLBody +=' Create Date | '
$HTMLBody +=' size | '
$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 +=' '+$VM+'.T-'+$k+".$7zipFileEnding | "
$HTMLBody +=' '+$FileDate+' | '
$HTMLBody +=' '+$FileSize+' GB | '
$HTMLBody +='
'
}
}
$HTMLBody +='
'
# Compare Report
$HTMLBody +='
Hyper-V-Backup Compare for all versions'
$HTMLBody +=''
$HTMLBody +=' '
$HTMLBody +=' VM Archive | '
$HTMLBody +=' VM Duplicate | '
$HTMLBody +=' Check Result | '
$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 +=' '+$ArchFile+' | '
$HTMLBody +=' '+$DuplFile+' | '
$HTMLBody +=' '+$CheckResult+' | '
$HTMLBody +='
'
}
} # schließe schleife für compare report
$HTMLBody +='
'
} # 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")
}