HyperV-Backup-Utility icon indicating copy to clipboard operation
HyperV-Backup-Utility copied to clipboard

Some feature requests

Open TripleNico opened this issue 1 year ago • 2 comments

First of all let me say i really like you're way of scripting and love to see your enthusiasm in your work.

Last year i've made my own version of you script then because i needed to be more lightweight and there were a few things not available then like the -LowDisk switch. But today i've check what you've been working at and i'm impressed, it almost fits my need and i know it's a lot to ask (since i can do it myself) but could you consider to add these features?

  • [x] Switch called -SendStatusUpdate: this features send after every VM a email with the status of that VM export. For example, in my case i have a few VM's that are TB's and the script runs at night. When it's morning i can see due to the emails where the backup script is in the process. I simply put a SendEmail at the end of the ForEach VM (Check second code block below)
  • [x] Switch calles -OptimizeVHD: this features uses the Optimize-VHD function before copying it to the backup destination. Example of the code i use inside the ForEach VM:
#Optimze the VHDX
Try {
    Write-Log -Type Info -Evt "Optimizing VHD(s)..."
    $VMVHDS = Get-VHD -Path $($Vm | Get-VMHardDiskDrive | Select-Object -ExpandProperty "Path")
    #Loop through each VHD(X) file of an VM an optimize it
    ForEach ($VHD in $VMVHDS) {
        #Now optimize the VHD to save space and therefore shorten the copy time
        Write-Log -Type Info -Evt "Used space before optimizing VHD [$($VHD.Path)] = $([math]::ceiling((Get-VHD -Path $VHD.Path).FileSize / 1GB )) GB"
        Optimize-VHD -Path "$($VHD.Path)" -Mode Full
        Write-Log -Type Info -Evt "Used space after optimizing VHD [$($VHD.Path)] = $([math]::ceiling((Get-VHD -Path $VHD.Path).FileSize / 1GB )) GB"
        $intTotalDisksSize += (Get-VHD -Path $VHD.Path).FileSize
    }
    Write-Log -Type Info -Evt "Done optimizing VHD(s)"
}
Catch {
    Write-Log -Type Err -Evt "Error during Optimize-VHD: $($_.Exception.Message)"
}
  • [x] Print the duration of how long it took per VM to back. In my script i simply compare time from the beginning of the ForEach VM and in one of the last lines print the duration. This gives me a pretty good idea how long it takes per VM. For example:
Write-Log -Type Info ""
Write-Log -Type Info "-------------------------- Processing VM: $Vm --------------------------------"
$StartTime = $(get-date)

... do stuff ...

$elapsedTime = $(get-date) - $StartTime
$totalTime = "{0:HH:mm:ss}" -f ([datetime]$elapsedTime.Ticks)
SendMail "VM $Vm done" "Processed VM: $Vm in $totalTime"
Write-Log -Type Info "-------------------------- Done processing VM: $Vm in $totalTime --------------------------------"
Write-Log -Type Info ""
  • [x] Switch -PrintVMInfo: this features simply printout all info it could find. For example what i use:
## Print info per VM.
ForEach ($Vm in $Vms) {
    #Get SystemInfo
    Try {
        #Log VM specs
        $VhdSize = Get-VHD -Path $($Vm | Get-VMHardDiskDrive | Select-Object -ExpandProperty "Path") | Select-Object @{Name = "FileSizeGB"; Expression = { [math]::ceiling( $_.FileSize / 1GB ) } }, @{Name = "M
        Write-Output "VM [$Vm] has [$((Get-VMProcessor $Vm).Count)] CPU cores, [$([math]::ceiling((Get-VMMemory $Vm).Startup / 1gb))GB] RAM and Storage [CurrentFileSizeGB = $($VhdSize.FileSizeGB)GB - MaxSizeG
    }
    Catch {
        Write-Error "Error during Systeminfo: $($_.Exception.Message)"
    }
}   
  • [ ] Extra funtions start Start and Stop a backupserver. This is something i have in my script because when the backuopserver isn't needed it is shutdown. (For security and power reasons). Snippit of the code:
function Invoke-WakeOnLan {
    param
    (
        # one or more MACAddresses
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        # mac address must be a following this regex pattern:
        [ValidatePattern('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$')]
        [string[]]
        $MacAddress 
    )
 
    begin {
        # instantiate a UDP client:
        # Support from PowerShell V5.X $UDPclient = [System.Net.Sockets.UdpClient]::new()
        $UDPclient = New-Object -TypeName System.Net.Sockets.UdpClient
    }
    process {
        foreach ($_ in $MacAddress) {
            try {
                $currentMacAddress = $_
        
                # get byte array from mac address:
                $mac = $currentMacAddress -split '[:-]' |
                # convert the hex number into byte:
                ForEach-Object {
                    [System.Convert]::ToByte($_, 16)
                }
 
                #region compose the "magic packet"
        
                # create a byte array with 102 bytes initialized to 255 each:
                $packet = [byte[]](, 0xFF * 102)
        
                # leave the first 6 bytes untouched, and
                # repeat the target mac address bytes in bytes 7 through 102:
                6..101 | Foreach-Object { 
                    # $_ is indexing in the byte array,
                    # $_ % 6 produces repeating indices between 0 and 5
                    # (modulo operator)
                    $packet[$_] = $mac[($_ % 6)]
                }
        
                #endregion
        
                # connect to port 400 on broadcast address:
                $UDPclient.Connect(([System.Net.IPAddress]::Broadcast), 4000)
        
                # send the magic packet to the broadcast address:
                $null = $UDPclient.Send($packet, $packet.Length)
                Write-Log -Type Info -Evt "Sent magic packet to $currentMacAddress..."
            }
            catch {
                Write-Log -Type Err -Evt "Unable to send ${mac}: $_"
            }
        }
    }
    end {
        # release the UDF client and free its memory:
        $UDPclient.Close()
        $UDPclient.Dispose()
    }
}

... later on ...

# Let's start the BackupServer if it's not running
If (Test-Connection -ComputerName "IP/Hostname" -Count 2 -Delay 5 -Quiet ) {
    #Backup server responded meaning it's already online.
    Write-Log -Type Info -Evt "BackupServer is online, waiting 30 seconds just to be sure..."
    Start-Sleep 30
}
else {
    Write-Log -Type Info -Evt "Sending magic packet to BackupServer [$BackupServerMacAddress]"
    Invoke-WakeOnLan -MacAddress "$BackupServerMacAddress" -Verbose
    #Now wait until backupserver is online
    Write-Log -Type Info -Evt "Waiting for BackupServer to come online..."
    If (Test-Connection -ComputerName "IP/Hostname" -Count 12 -Delay 10 -Quiet ) {
        #Backupserver responded now give some extra time for Windows to fully be up and running
        Write-Log -Type Info -Evt "BackupServer is online, waiting 2 more minutes for Windows to complete..."
        Start-Sleep 120
    }
    else {
        Write-Log -Type Err -Evt "Backupserver failed to respond within 2 minutes. BACkUP SCRIPT WILL NOT CONTINUE"
        $MailBody = Get-Content -Path $Log | Out-String
        SendMail "VM Backup: Failed to start backupserver" $MailBody
        Write-Log -Type Info -Evt "Log finished"
        Exit
    }
}


... When done, shutdown ...

## Now shutdown the backup server
Try {
    Write-Log -Type Info -Evt "Waiting for BackupServer to shutdown..."
    Stop-Computer -ComputerName "IP/Hostname" -Credential $credObject -Force
    #Wait until backupserver is down
    While ( Test-Connection "IP/Hostname" -Quiet -Count 1) {
        Start-Sleep -Seconds 10
        #Check if we are waiting more than two minutes
        if ($timer.Elapsed.TotalSeconds -gt 120) {
            Write-Log -Type Err -Evt "Backupserver shutdown did not complete before timeout period."
            Break
        }
    }
    #Double check if server is down or Watchdog has kicked in
    if (-Not (Test-Connection "IP/Hostname" -Quiet -Count 2)) {
        Write-Log -Type Info -Evt "BackupServer succesfully shutdown"
    }
}
catch {
    Write-Log -Type Err -Evt "Error backup shutdown: $($_.Exception.Message)"
}

Those are the points i have, not to bad right ;-)

Again, thanks for all your effort so far!

TripleNico avatar Apr 29 '23 16:04 TripleNico