# Backup Hyper-V VMs one at a time by suspending VM, exporting it to a location and resuming VMs. # Author: Morgan Robertson # Date: 06/11/2015 # Script settings: [String]$BackupPath = "D:\Backups\" [String]$ErrorLog = "D:\Backups\" [String[]]$EmailRecipient = @("itcontact_1@company.com", "itcontact_2@company.com") [String]$SMTPServer = "relay.company.com" [String]$EmailFromAddress = "it@company.com" [Boolean]$RemoveOldFiles = False [Int]$RemoveFilesOlderThanNumDays = 90 # Acquire current date, set log file $StartDate = Get-Date -format yyyy-MM-dd $ErrorLog = "$ErrorLog" + "Hyper-V-" + "$StartDate" + ".log" "$(Get-Date) - Hyper-V Backups Starting. `r`n" | Out-File $ErrorLog -Append # Get backup path and clear old existing backup from path if Boolean set If ($RemoveOldFiles = True) { Try { $limit = (Get-Date).AddDays($RemoveFilesOlderThanNumDays * -1) # Delete files older than the $limit. Get-ChildItem -Path $BackupPath -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force # Delete any empty directories left behind after deleting the old files. Get-ChildItem -Path $BackupPath -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse } Catch { "$(Get-Date) - Error during removing old backups: $($_.Exception.Message)" | Out-File $ErrorLog -Append Return } } # Set Backup Path for current date's backup and create folder $BackupPath = $BackupPath + "Hyper-V-" + $StartDate + "\" "$(Get-Date) - Backup location is $BackupPath`r`n" | Out-File $ErrorLog -Append New-Item $BackupPath -type directory # Get list of all running VMs and store into an array: Try { $List_of_VMs = Get-VM | Where { $_.State –eq ‘Running’ } | select -expand Name } Catch { "$(Get-Date) - Error during Get-VM to acquire list of VM names: $($_.Exception.Message)" | Out-File $ErrorLog -Append } # Loop through each VM, save, export and resume VM one at a time Foreach ($VM_name in $List_of_VMs) { Write-Host "`r`nProcessing VM: $VM_name" "$(Get-Date) - Backing up VM $VM_name" | Out-File $ErrorLog -Append # Suspend VM Try { Stop-VM -Name $VM_name -ErrorAction Stop -Save Write-Host "`r`nStop-VM -Name $VM_name -ErrorAction Stop -Save" "$(Get-Date) - VM Suspended" | Out-File $ErrorLog -Append } Catch { "$(Get-Date) - Error during VM Save: $($_.Exception.Message)" | Out-File $ErrorLog -Append Continue } # Export VM Try { Export-VM -Name $VM_name -ErrorAction Stop -Path $BackupPath Write-Host "`r`nExport-VM -Name $VM_name -ErrorAction Stop -Path $BackupPath" "$(Get-Date) - VM Exported" | Out-File $ErrorLog -Append } Catch { "$(Get-Date) - Error during Export-VM: $($_.Exception.Message)" | Out-File $ErrorLog -Append Continue } # Resume VM Try { Start-VM -Name $VM_name -ErrorAction Stop Write-Host "`r`nStart-VM -Name $VM_name -ErrorAction Stop" "$(Get-Date) - VM Resumed" | Out-File $ErrorLog -Append } Catch { "$(Get-Date) - Error during VM Resumation: $($_.Exception.Message)" | Out-File $ErrorLog -Append Continue } # Write blank line in log " " | Out-File $ErrorLog -Append } # Write end timestamp to log "`n$(Get-Date) - Hyper-V Backups completed" | Out-File $ErrorLog -Append # Send E-mail with log attached Try { Send-MailMessage -To $EmailRecipient -Subject "$env:computername Hyper-V Backup for $StartDate" -From $EmailFromAddress -SmtpServer $SMTPServer -Attachments $ErrorLog -Body "See attached file for a log of today's Hyper V Backup" } Catch { "`r`n$(Get-Date) - Can't send e-mail message: $($_.Exception.Message)" | Write-Host } # Debug Code: Wait for user recognition of script success or failure # Write-Host "Press any key to continue ..." # $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")