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.

41_1

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
Nickolaj Andersen
Principal Consultant and Enterprise Mobility MVP. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows deployments and Automation. In 2015 Nickolaj was awarded as PowerShell Hero by the community for his script and tools contributions. Author of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService and a frequent speaker at user groups.

(15)

comments
  • Joe Gasper
    Posted at 07:22 July 5, 2013
    Joe Gasper
    Reply
    Author

    How about taking a VSS snapshot, mounting the snapshot, and then backing up the VMs…

    • Nickolaj
      Posted at 09:09 July 5, 2013
      Nickolaj
      Reply
      Author

      That’d probably be the absolute best way to do it.

      /Nickolaj

  • niels
    Posted at 20:38 February 15, 2014
    niels
    Reply
    Author

    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.

    • Nickolaj
      Posted at 23:57 February 15, 2014
      Nickolaj
      Reply
      Author

      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

  • Mike
    Posted at 13:17 May 26, 2015
    Mike
    Reply
    Author

    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

  • Leave a Reply