During the past few weeks Microsoft has released a lot of new cool Previews of their upcoming software. For that reason my lab environment has grown quite large, and it’s no longer effective to manually back it up. So I turned to PowerShell and wrote a script that will take care of that for me.
What the script does
The script will put all your VM’s that are running in a Saved state, export them to a given folder and then start them again (it will also export ones that are in the Off state, but not start them). In the example code further down, you’ll see that I’m exporting the VM’s to my Qnap NAS device. When you run the script, it will create a “day dir” and export each VM into a separate subfolder. If you’d set C:\Backup as the value of $BackupFolder in the script, the folder structure would be as follows:
C:\Backup\2013-07-03\<name_of_your_vm>
The script will also log certain thing it does, even errors, to C:\Backup\LogFile.txt (if that’s the root folder specified).
Get it to work
In order to get the script working, you’d need to alter two variables. The value of $BackupFolder should be set to a folder which will be used as the root for all the backups. $PSDrivePath needs to set if $BackupFolder points to an mapped network drive. In my example code below, the setup looks like this:
- \\qnap01\Backup – mapped as N: to my workstation running Windows 8 with Hyper-V
- \\qnap01\Backup\Hyper-V Backup is my root folder.
In order to try and illustrate it better, a picture always works best.
The script
Save the below script as Export-LabEnvironment.ps1 to your computer. I’d recommend that you run this as a scheduled task on a certain schedule of your choosing.
$Error.Clear() $WarningPreference = "SilentlyContinue" $Date = Get-Date -Format d $StringDate = $Date.ToString() $BackupFolder = "\\qnap01\Backup\Hyper-V Backup" $LogFile = "$($BackupFolder)\LogFile.txt" $BackupSubFolder = "$($BackupFolder)\$Date" $PSDrivePath = "\\qnap01\Backup" try { if (Test-Path -Path $PSDrivePath) { New-PSDrive -Name N -PSProvider FileSystem -Root $PSDrivePath -ErrorAction Stop | Out-Null } else { Write-Output "$($PSDrivePath) not found." } } catch { Write-Output "WARNING: $($_.Exception.Message)" } try { if (!(Test-Path -Path $LogFile)) { New-Item -ItemType File -Path $LogFile -ErrorAction Stop | Out-Null } } catch { Write-Output "$(Get-Date -Format G) ## ERROR: $($_.Exception.Message)" | Out-File $LogFile -Append } if (Test-Path -Path $LogFile) { Write-Output "$(Get-Date -Format G) ## Starting backup of lab environment." | Out-File $LogFile -Append Write-Output "$(Get-Date -Format G) ## -----------------------------------------" | Out-File $LogFile -Append } try { $SubFolder = Get-ChildItem $BackupFolder -Name "$StringDate" if (-not($SubFolder -like "$($StringDate)")) { Write-Output "$(Get-Date -Format G) ## Creating the $($Date) folder." -ErrorAction Stop | Out-File $LogFile -Append New-Item -ItemType Directory -Path $BackupSubFolder -ErrorAction Stop | Out-Null } } catch { Write-Output "$(Get-Date -Format G) ## ERROR: $($_.Exception.Message)" | Out-File $LogFile -Append } try { Get-VM | ForEach-Object { if ($_.State -like "Running") { Write-Output "$(Get-Date -Format G) ## $($_.Name) is currently running, will stop it." -ErrorAction Stop | Out-File $LogFile -Append Stop-VM -Name $_.Name -Save -ErrorAction Stop Write-Output "$(Get-Date -Format G) ## Exporting $($_.Name) to $($BackupSubFolder)." -ErrorAction Stop | Out-File $LogFile -Append Export-VM -Name $_.Name -Path $($BackupSubFolder) -ErrorAction Stop Write-Output "$(Get-Date -Format G) ## Starting $($_.Name)." -ErrorAction Stop | Out-File $LogFile -Append Start-VM -Name $_.Name -ErrorAction Stop } elseif ($_.State -like "Off") { Write-Output "$(Get-Date -Format G) ## $($_.Name) is already turned off." -ErrorAction Stop | Out-File $LogFile -Append Write-Output "$(Get-Date -Format G) ## Exporting $($_.Name) to $($BackupSubFolder)." -ErrorAction Stop | Out-File $LogFile -Append Export-VM -Name $_.Name -Path $($BackupSubFolder) -ErrorAction Stop } } } catch { Write-Output "$(Get-Date -Format G) ## ERROR: $($_.Exception.Message)" | Out-File $LogFile -Append } if ($Error[0]) { if (Test-Path -Path $LogFile) { Write-Output "$(Get-Date -Format G) ## Errors have occured during backup." | Out-File $LogFile -Append Write-Output "$(Get-Date -Format G) ## -----------------------------------------" | Out-File $LogFile -Append } } else { if (Test-Path -Path $LogFile) { Write-Output "$(Get-Date -Format G) ## Backup completed successfully." | Out-File $LogFile -Append Write-Output "$(Get-Date -Format G) ## -----------------------------------------" | Out-File $LogFile -Append } } $Error.Clear() Remove-PSDrive -Name N -Force | Out-Null
This script is awesome. Although the date format gave me some trouble I ran it and it created several subfolders based on the date. In your screenshot there is a backup folder “2013-07-03”. When I ran the script (on 26 May 2015) it created a folder “5”, inside of that a folder “26” and inside of that a folder “2015” and in that folder it created the backups.
G:\Hyper-V Backups\5\26\2015
5/26/2015 8:07:25 AM ## Starting backup of lab environment.
5/26/2015 8:07:25 AM ## —————————————–
5/26/2015 8:07:25 AM ## Creating the 5/26/2015 folder.
5/26/2015 8:07:25 AM ## DEMO-CM01 is currently running, will stop it.
5/26/2015 8:07:33 AM ## Exporting DEMO-CM01 to G:\Hyper-V Backup\5/26/2015.
I just forced the format for the Get-Date command to match your formatting:
#$Date = Get-Date -Format d
$Date = Get-Date -Format yyyy-mm-dd
For me it doesnt work on windows 8.1. I get nothing in return, no error code. So when I run it, It gives me a new command prompt instantly. I dont know how to explain exactly but it doesnt do antyhing and my vms keep running.
Hi Niels,
Could you please send me your script-file to my email address found on the about page? I’ll take a look and see if I can figure it out.
Regards,
Nickolaj
How about taking a VSS snapshot, mounting the snapshot, and then backing up the VMs…
That’d probably be the absolute best way to do it.
/Nickolaj