Trouble Exporting VM's w/ powershell

Anyone know why this script doesn’t work? Its designed to execute on a target server, stop the hyper-v vm’s, then copy them to a network drive. The script stops the vm, creates the folders in the desired network location, but does not move the backups.

error : "export-vm : Export failed for virtual machine ‘Test Web Server’.
Export failed for virtual machine ‘Test Web Server’ (9F1BF860-D37D-47EE-9637-EB7F3ECFF2DD) with error ‘The request is not supported.’
(0x80070032).
At C:\VM-Export-And-Backup.ps1:417 char:25

  • … export-vm -Name $VMName -ComputerName $VMHost -path $Dest …
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (:slight_smile: [Export-VM], VirtualizationException
    • FullyQualifiedErrorId : NotSupported,Microsoft.HyperV.PowerShell.Commands.ExportVM"

Powershell script ~

$Logfile = ""





Function Backup-VMs
{
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [parameter(Mandatory = $true)]
        [string]$BackupDriveLetter,                     # $BackupDriveLetter 

        [ValidateNotNullOrEmpty()]
        [string]$VMHost,                                        # the host that holds the vms we wish to back up, otherwise the one running the script
        [string[]]$VMNames,                                     # if not specified, back up all of them
        [switch]$ShutHostDownWhenFinished       # when set, shuts down the target host, including any vms on it
        )
    process
    {
        # first, run a bunch of checks

        # check if the .Net 4.5 is installed
        $isDotNet45Installed = Test-Net45
        if (!$isDotNet45Installed)
        {
            Write-Host -ForegroundColor Red ".Net 4.5 is not installed - terminating backup script."
            Break
        }
        # check if the HyperV module is loaded
        $isHyperVModuleLoaded = Get-Module -Name Hyper-V
        if (!$isHyperVModuleLoaded)
        {
            Write-Host "Attempting to load Hyper-V module..."
            Import-Module -Name Hyper-V
            $isHyperVModuleLoaded = Get-Module -Name Hyper-V
            if (!$isHyperVModuleLoaded)
            {
                Write-Host -ForegroundColor Red "Cannot load Hyper-V module - terminating backup script."
                Break
            }
        }
        # sanitize user input (F: will become F)
        if ($BackupDriveLetter -like "*:")
        {
            $BackupDriveLetter = $BackupDriveLetter -replace ".$"
        }
        # check to make sure the user specified a valid backup location
        if ((Test-Path "$($BackupDriveLetter)") -eq $false)
        {
            Write-Host -ForegroundColor Red "Drive $($BackupDriveLetter) does not exist - terminating backup script."
            Break
        }
        # if host was not speicified, use the host running the script
        if ($VMHost -eq "")
        {
            $VMHost = Hostname
        }
        # check to make sure the specified host is a vmhost
        if (!(Get-VMHost) -icontains $VMHost)
        {
            Write-Host -ForegroundColor Red "Host $($VMHost) is not listed in Get-VMHost - terminating backup script."
            Break
        }
        # check to make sure the specified host has any vms to back up
        if (!(Get-VM -ComputerName $VMHost))
        {
            Write-Host -ForegroundColor Red "Host $($VMHost) does not appear to have any VMs running on it according to 'Get-VM -ComputerName $($VMHost)'."
            Write-Host -ForegroundColor Yellow "This can be occur if PowerShell is not running with elevated privileges."
            Write-Host -ForegroundColor Yellow "Please make sure that you are running PowerShell with Administrator privileges and try again."
            Write-Host -ForegroundColor Red "Terminating backup script."
            Break
        }
        #endregion

        #region directory business
        # make our parent directory if needed
        if ((Test-Path "$($BackupDriveLetter)\test") -eq $false)
        {
            $parentDir = New-Item -Path "$($BackupDriveLetter)\test" -ItemType "directory"
            if ((Test-Path $parentDir) -eq $false)
            {
                Write-Host -ForegroundColor Red "Problem creating $parentDir - terminating backup script."
                Break
            }
        }

        # initialize our logfile
        $Logfile = "$($BackupDriveLetter)\test\Backup-VMs.log"
        if ((Test-Path $Logfile) -eq $false)
        {
            $newFile = New-Item -Path $Logfile -ItemType "file"
            if ((Test-Path $Logfile) -eq $false)
            {
                Write-Host -ForegroundColor Red "Problem creating $Logfile - terminating backup script."
                Break
            }
        }

        $backupDate = Get-Date -Format "yyyy-MM-dd"
        $destDir = "$($BackupDriveLetter)\test\$backupDate-$VMHost-backup\"

        # make our backup directory if needed
        if ((Test-Path $destDir) -eq $false)
        {
            $childDir = New-Item -Path $destDir -ItemType "directory"
            if ((Test-Path $childDir) -eq $false)
            {
                Write-Host -ForegroundColor Red "Problem creating $childDir - terminating backup script."
                Break
            }
        }
        #endregion

        Add-content -LiteralPath $Logfile -value "==================================================================================================="
        Add-content -LiteralPath $Logfile -value "==================================================================================================="
        # now that our checks are done, start backing up
        T -text "Starting Hyper-V virtual machine backup for host $VMHost at:"
        $dateTimeStart = date
        T -text "$($dateTimeStart)"
        T -text ""

        # export the vms to the destination
        ExportMyVms -VMHost $VMHost -Destination $destDir -VMNames $VMNames

        T -text ""
        T -text "Exporting finished"

        #region compression

        # get what we just backed up
        $sourceDirectory = Get-ChildItem $destDir

        if ($sourceDirectory)
        {
            # get the total size of all of the files we just backed up
            $sourceDirSize = Get-ChildItem $destDir -Recurse | Measure-Object -property length -sum -ErrorAction SilentlyContinue
            $sourceDirSize = ($sourceDirSize.sum / 1GB)

            # get how much free space is left on our backup drive
            $hostname = Hostname
            $backupDrive = Get-WmiObject win32_logicaldisk -ComputerName $hostname | Where-Object { $_.DeviceID -eq "$($BackupDriveLetter)" }
            $backupDriveFreeSpace = ($backupDrive.FreeSpace / 1GB)

            # tell the user what we've found
            $formattedBackupDriveFreeSpace = "{0:N2}" -f $backupDriveFreeSpace
            $formattedSourceDirSize = "{0:N2}" -f $sourceDirSize
            T -text "Checking free space for compression:"
            T -text "Drive $($BackupDriveLetter) has $formattedBackupDriveFreeSpace GB free on it, this backup took $formattedSourceDirSize GB"

            # check if we need to make any room for the next backup
            $downToOne = $false
            while (!$downToOne -and $sourceDirSize > $backupDriveFreeSpace)
            {
                # clear out the oldest backup if this is the case
                $backups = Get-ChildItem -Path "$($BackupDriveLetter)\test\" -include "*-backup.zip" -recurse -name
                $backups = [array]$backups | Sort-Object

                # make sure we aren't deleting the only directory!
                if ($backups.length -gt 1)
                {
                    T -text "Removing the oldest backup [$($backups[0])] to clear up some more room"
                    Remove-Item "$($BackupDriveLetter)\test\$($backups[0])" -Recurse -Force
                    # now check again
                    $backupDrive = Get-WmiObject win32_logicaldisk -ComputerName $hostname | Where-Object { $_.DeviceID -eq "$($BackupDriveLetter)" }
                    $backupDriveFreeSpace = ($backupDrive.FreeSpace / 1GB)
                    $formattedBackupDriveFreeSpace = "{0:N2}" -f $backupDriveFreeSpace
                    T -text "Now we have $formattedBackupDriveFreeSpace GB of room"
                }
                else
                {
                    # we're down to just one backup left, don't delete it!
                    $downToOne = $true
                }
            }
            T -text "Compressing the backup..."
            # zip up everything we just did
            ZipFolder -directory $destDir -VMHost $VMHost

            $zipFileName = $destDir -replace ".$"
            $zipFileName = $zipFileName + ".zip"

            T -text "Backup [$($zipFileName)] created successfully"
            $destZipFileSize = (Get-ChildItem $zipFileName).Length / 1GB
            $formattedDestSize = "{0:N2}" -f $destZipFileSize
            T -text "Uncompressed size:`t$formattedSourceDirSize GB"
            T -text "Compressed size:  `t$formattedDestSize GB"
        }
        #endregion

        # delete the non-compressed directory, leaving just the compressed one
        Remove-Item $destDir -Recurse -Force

        T -text ""
        T -text "Finished backup of $VMHost at:"
        $dateTimeEnd = date
        T -text "$($dateTimeEnd)"
        $length = ($dateTimeEnd - $dateTimeStart).TotalMinutes
        $length = "{0:N2}" -f $length
        T -text "The operation took $length minutes"

        if ($ShutHostDownWhenFinished -eq $true)
        {
            T -text "Attempting to shut down host machine $VMHost"
            ShutdownTheHost -HostToShutDown $VMHost
        }
    }
}


Function ShutdownTheHost
{
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [string]$HostToShutDown
        )
    process
    {
        ## Get a list of all VMs on $HostToShutDown
        $VMs = Get-VM -ComputerName $HostToShutDown
        ## only run through the list if there's anything in it
        if ($VMs)
        {
            ## For each VM on Node, Save (if necessary), Export and Restore (if necessary)
            foreach ($VM in @($VMs))
            {
                $VMName = $VM.ElementName
                $summofvm = get-vm -Name $VMName | select *
                $summhb = $summofvm.heartbeat
                $summes = $summofvm.state

                ## Shutdown the VM if HeartBeat Service responds
                if ($summhb -eq "OK")
                {
                    T -text ""
                    T -text "HeartBeat Service for $VMName is responding $summhb, saving the machine state"

                    Stop-VM -VMName $VMName -ComputerName $VMHost -Force
                }
                ## Checks to see if the VM is already stopped
                elseif (($summes -eq "Stopped") -or ($summes -eq "Suspended"))
                {
                    T -text ""
                    T -text "$VMName is $summes"
                }

                ## If the HeartBeat service is not OK, aborting this VM
                elseif ($summhb -ne "OK" -and $summes -ne "Stopped")
                {
                    T -text
                    T -text "HeartBeat Service for $VMName is responding $summhb. Aborting save state."
                }
            }
            T -text "All VMs on $HostToShutDown shut down or suspended."
        }
        T -text "Shutting down machine $HostToShutDown..."
        Stop-Computer -ComputerName $HostToShutDown
    }
}

function ZipFolder(
    [IO.DirectoryInfo] $directory)
{    
    $backupFullName = $directory.FullName

    T -text ("Creating zip file for folder ($backupFullName)...")

    [IO.DirectoryInfo] $parentDir = $directory.Parent

    [string] $zipFileName

    If ($parentDir.FullName.EndsWith("\") -eq $true)
    {
        # e.g. $parentDir = "C:\"
        $zipFileName = $parentDir.FullName + $directory.Name + ".zip"
    }
    Else
    {
        $zipFileName = $parentDir.FullName + "\" + $directory.Name + ".zip"
    }

    Add-Type -Assembly System.IO.Compression.FileSystem
    $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
    [System.IO.Compression.ZipFile]::CreateFromDirectory($directory, $zipFileName, $compressionLevel, $false)

    T -text "Successfully created zip file for folder ($backupFullName)."
}


{
    if (Test-Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full')
    {
        if (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Release -ErrorAction SilentlyContinue)
        {
            return $True
        }
        return $False
    }
}


Function ExportMyVms
{
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [string]$Destination,
        [string[]]$VMNames,
        [string]$VMHost
        )
    process
    {              
        ## Get a list of all VMs on Node
        if ($VMNames)
        {
            if (($VMNames.Length) -gt 1)
            {
                # pass in a multiple-element string array directly
                $VMs = Get-VM -Name $VMNames -ComputerName $VMHost
            }
            else
            {
                # turn a single-element string array back into a string
                $VMNames = [string]$VMNames
                $VMs = Get-VM -Name "$VMNames" -ComputerName $VMHost
            }
        }
        else
        {
            $VMs = Get-VM -ComputerName $VMHost
        }

        ## only run through the list if there's anything in it
        if ($VMs)
        {
            foreach ($VM in @($VMs))
            {
                $listOfVmNames += $VM.Name + ", "
            }
            $listOfVmNames = $listOfVmNames -replace "..$"
            T -text "Attempting to backup the following VMs:"
            T -text "$listOfVmNames"
            T -text ""
            Write-Host "Do not cancel the export process as it may cause unpredictable VM behavior" -ForegroundColor Yellow

            ## For each VM on Node, Save (if necessary), Export and Restore (if necessary)
            foreach ($VM in @($VMs))
            {
                $VMName = $VM.Name
                $summofvm = get-vm -Name $VMName | select *
                $summhb = $summofvm.heartbeat
                $summes = $summofvm.state
                $restartWhenDone = $false

                $doexport = "no"

                ## Shutdown the VM if HeartBeat Service responds
                if (($summhb -eq "OK") -or ($summhb -like "ok*"))
                {
                    $doexport = "yes"
                    T -text ""
                    T -text "HeartBeat Service for $VMName is responding $summhb, saving the machine state"
                    $restartWhenDone = $true

                    Stop-VM -VMName $VMName -ComputerName $VMHost -Force

                    #Wait until the machine is Off
                    T -text "Waiting for shutdown operation to finish "
                    While ((Get-VM -VMName $VMName -ComputerName $VMHost).State -ne "Off")
                    {
                        Write-Host "." -NoNewLine
                        Start-Sleep -Seconds 5
                    }
                }

                ## Checks to see if the VM is already stopped
                elseif (($summes -eq "Stopped") -or ($summes -eq "Suspended") -or ($summes -eq "Off"))
                {
                    $doexport = "yes"
                    T -text ""
                    T -text "$VMName is $summes, starting export"
                }

                ## If the HeartBeat service is not OK, aborting this VM
                elseif ($summhb -ne "OK" -and $summes -ne "Stopped")
                {
                    $doexport = "no"
                    T -text ""
                    T -text "HeartBeat Service for $VMName is responding $summhb. Save state and export aborted for $VMName"
                }

                if ($doexport -eq "yes")
                {
                    $VMEnabledState = (Get-VM -VMName $VMName -ComputerName $VMHost).State

                    T -text $VMEnabledState
                    
                    if (("Suspended", "Stopped", "Off") -contains $VMEnabledState)
                    {
                        ## If a folder already exists for the current VM, delete it.
                        if ([IO.Directory]::Exists("$($Destination)\$($VMName)"))
                        {
                            [IO.Directory]::Delete("$($Destination)\$($VMName)", $True)
                        }
                        
                        T -text "Exporting $VMName"

                        T -text "VMName: $VMName"
                        T -text "VMHost: $VMHost"
                        T -text "Destination: $Destination"

                        ## Begin export of the VM
                        export-vm -Name $VMName -ComputerName $VMHost -path $Destination #-ErrorAction SilentlyContinue

                        ## check to ensure the export succeeded
                        $exportedCount = (Get-ChildItem $Destination -Force -Recurse).Count

                        ## there should be way more than 5 elements in the destination - this is to account for empty folders
                        if ($exportedCount -lt 5)
                        {
                            T -text "***** Automated export failed for $VMName *****"
                            T -text "***** Manual export advised *****"
                        }

                        if ($restartWhenDone)
                        {
                            T -text "Restarting $VMName..."

                            ## Start the VM and wait for a Heartbeat with a 5 minute time-out
                            # Start-VM $VMName -HeartBeatTimeOut 300 -Wait
                            Start-VM $VMName -Wait
                        }
                    }
                    else
                    {
                        T -text "Could not properly save state on VM $VMName, skipping this one and moving on."
                    }
                }
            }
        }
        else
        {
            T -text "No VMs found to back up."
        }
    }
}


Function T
{
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [string]$text
        )
    process
    {
        Write-Host "$text"
        $now = date
        $text = "$now`t: $text"
        Add-content -LiteralPath $Logfile -value $text
    }
}

Backup-VMs -BackupDriveLetter \\192.168.1.6\IT_Share\

Thank you in Advance

jamesl,
Welcome to the forum. :wave:t4:

Before we proceed - please go back and edit your post to fix the formatting of your code.
When you post code or sample data or console output please format it as code using the preformatted text button ( </> ). Simply place your cursor on an empty line, click the button and paste your code.
Thanks in advance

What have you tried to find the error cause by yourself? Did you try to search for it? When I google it it appears to be an issue with the SMB share - not with the PowerShell code.

https://social.technet.microsoft.com/Forums/sqlserver/en-US/f8764706-a47d-4664-ab84-7960aab198ec/exporting-hyperv-vms-using-exportvm-to-a-seagate-nas-device?forum=winserverhyperv

So it might be helpful as well to ask this question in a forum related to your NAS.

1 Like

Thanks! Fixed the formatting.

I figured it might be SMB, but per MS I enabled SMB 2 & 3 on the servers and the shared location. Originally it was a permissions issue, but fixed by providing the access required. Then I ended up getting the “(0x80070032)” error. SMB related.

Saving these locally or to any other Windows server works (script works), but space becomes an issue on windows servers due to the size of the VM’s. Pointing the script to the NAS is ideal in my environment.

Appreciate the link

Greeat. Thanks. :+1:t4: Doesn’t it look much better like this? :wink: :grin:

Does it work when copy the backup manually to the NAS share?

Could this be a Kerberos double hop issue?