Export-VM on Clustered Hyper-V

Sep 11, 2011 at 4:02 PM

I am trying to schedule a task so that all VMs running on the clustered Hyper-V hosts (2008 R2, Datacenter ed.) will be exported once a week.

What I've done is:
1. Install Hyperv.ps1 on the both cluster nodes
2. Write a PS script (See below)
3. Create a task on both nodes so that the script runs weekly


My problem is that the script starts as scheduled but all lines including the HyperV module (from the line $VMs = Get-vm to Start-VM $VM -HeartBeatTimeOut 300) are not executed.

Strange things are that when I run the script from the PowerShell console on the cluster, it completes, thus VMs are all exported. Also when the script runs by schedule on a non-clustered Hyper-V host, VMs are exported.
It seems only when the script runs by schedule on the cluster, VMs will not be exported because the codes including Hyperv module are not executed.

I wonder if I miss anything?
If anybody knows what causes this, please help me out.

Thanks!

 
Here is my script:

---------------------------------------------------------------------------------------------------------------------------------

############ ExportVMs.ps1 ################################

# This script export all VMs on this local Hyper-V host

# Exported VMs will be stored in \\Backup

###########################################################

 

Set-ExecutionPolicy unrestricted -force

 

# Module "Hyperv.psd1" is located:

# C:\Users\administrator.MyDomain\Documents\WindowsPowerShell\Modules\HyperV

if (!(Get-Module -Name hyperv))

    {import-module hyperv}

 

$date = Get-Date -format yyyy-MM-dd

$backupDir = "\\Backup \VM_Backup"

New-Item -path $backupDir\$date -type directory -force

Start-Transcript -path $backupDir\$date.txt

 

$VMs = Get-vm

foreach ($VM in $VMs)

{

    if((Get-VMSummary $VM).enabledstate -eq "running")

    {

        invoke-vmshutdown $VM -force

        export-VM -VM $VM -path $backupDir\$date -force -copystate -wait

        Start-VM $VM -HeartBeatTimeOut 300   

    } 

}

Stop-Transcript

---------------------------------------------------------------------------------------------------------------------------------

 


 

Oct 6, 2011 at 12:54 PM
Edited Nov 9, 2011 at 10:53 AM

You try to write

$VMs = Get-vm

but this commandlet get for you all information about all of VMs. You can see the result of this, typing in PS console:

$VMs = Get-vm

$VMs

To get the result what you need (the list with names of VMs) you have to select parameter VMElementName of Get-VM commandlet. See:

$VMs = Get-vm | Select-Object -Property VMElementName 

# Now you have your own class with VMs names only. And the next string must be

foreach ($VM in $VMs.VMElementName)

{

blah-blah-blah... (your code)

}

I've take your code for my server. Many thanks. Hope my advice helps you.

P.S. Sorry or my bad English ;-)

Nov 8, 2011 at 9:11 AM

Hello!!

 

I 'm interested in using this script, I have a little problem, hope you could help me: This line is not working for me. The "-Parameter" is not recognized in my environment.

$VMs = Get-vm | Select-Object -Parameter VMElementName 

# Now you have your own class with VMs names only. And the next string must be

foreach ($VM in $VMs.VMElementName)

Thanks you very much and excuse my english.

 

Regards

Nov 9, 2011 at 10:58 AM

Sorry, I made mistake with name of the cmdlet parameter.

I have been change my previous post to right. 

Also, you can get the information about parameter by youself and fix it. Just type: Get-Help Select-Object -Detailed

The right string is: 

$VMs = Get-vm | Select-Object -Property VMElementName

Sorry :-)

Nov 10, 2011 at 8:22 AM

Thank you very much,

Sorry, I'm new in powershell.

I'm trying a disaster recovery solultion for a customer.

2 Datacenters, 2 Clusters with CSV, LUN replicated to the other Datacenter (using storage replication from two 3PAR or HP EVA)

My idea is to export all VMs (only config)  in a High available repository.

In a Datacenter failure scenario, I would change replica to RW to the failover datacenter, present the CSV volume to the other cluster and try to import the VMs and try to power on.

I think may work, and could be a good solution.

I will post if works to could help people in the same situation as me.

Thanks all, and excuse my english...

Regards,

 

 

 

Nov 11, 2011 at 8:16 AM

Will wait for yours answer. Do you speak russian? :-)

Jan 21, 2012 at 4:36 AM

Hi!

 

I'm very interested on the final version of the script. It's just what I was looking for! Could someone share it somehow or mail it to me?

Thanks a lot!

 

Regards,

 

Luis Talora

 

Feb 10, 2012 at 12:38 PM

I wrote a script for this you may be interested in. You can find it here: http://www.czerno.com/html/windows/hyperv/cluster/HyperV_Export_VMs.asp

 

Feb 11, 2012 at 4:14 PM

Hello!

Here is my script:

#Writen by Marek Astashev

param ($VM)

if (!(Get-Module -Name hyperv))
    {
        import-module hyperv
    }
if($VM -eq $null)
    {
        Write-Host "Enter the name of your VM!"
    }
   
$ComputerName = Get-Content env:COMPUTERNAME
# Connect network disk to get info about VHD
Invoke-Expression -Command "net use R: \\Server /y"
$count = $false # Trigger of VM whitch was founded
$export_count = $false # Trigger for successful export VM
# Set name of log-file
$log = "\\Server\backup\Logs\$VM\" + (Get-Date -Format "yyy-MM") +".log"
# Export path
$BackupDir = "\\Server\backup\"+$VM
# Path to VHD for get info
$InfoDir = "R:\backup\"+$VM
# Data for email
$Sender = "BackupOperator@Contoso.com"
$SMTP = "smtp.contoso.com"
$Recipients = @("backup_monitoring@contoso.com")
$AttachePath = "R:\backup\Logs\$VM\" + (Get-Date -Format "yyy-MM") +".log"

function SendEmail($Subject,$Body,$Priority)
    {
        $Message = New-Object System.Net.Mail.MailMessage
        $Server = New-Object System.Net.Mail.SMTPClient
        $Attache = New-Object System.Net.Mail.Attachment($AttachePath)
        # ==== Message
        $Recipients | ForEach-Object {$Message.To.Add($_)}
        $Message.From = $Sender
        $Message.Subject = $Subject
        $Message.Body = $Body
        $Message.Attachments.Add($Attache)
        $Message.Priority = [System.Net.Mail.MailPriority]::$Priority
        # ==== Sending
        $Server.Host = $SMTP
        $Server.Send($Message)
    }

# =========================================
# Start to write log-file
$log_string = $log_string + (Get-Date) + "`r`n Starting... "+$VM + " ServerName: " + $ComputerName + "`r`n Get the list of VMs... `r`n"

# Get the list of VMs
$VMArray = Get-VM | Select-Object -Property VMElementName
foreach($element in $VMArray)
{
    # Search given VM
    if($VM -eq $element.VMElementName)
    {
        $log_string = $log_string + "VM was found. Start working... `r`n"
        $count = $true           
# ========== Operations with folders ===============

        # Delete Yesterday Folder
        Remove-Item -Path ($BackupDir + "\Yesterday") -Recurse -ErrorVariable err
        if($err)
        {
            # If get error - stop!
            $log_string = $log_string + $err + "`r`n"
            $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Deleting Yesterday folder was unsuccessful, break exporting $VM. Error: " + $err
        }
        else
        {
            $log_string = $log_string + "Yesterday folder was successfully deleted. `r`n"
            # Rename Today Folder
            Rename-Item -Path ($BackupDir + "\Today") -NewName ($BackupDir + "\Yesterday") -Force -ErrorVariable err
            if($err)
            {
                $log_string = $log_string + $err + "`r`n"
                $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Renaming Yesterday folder was unsuccessful, break exporting $VM. Error: " + $err
            }
            else
            {
                $log_string = $log_string + "Yesterday folder was successfully renamed. `r`n"
                New-Item -Path ($BackupDir + "\Today") -ItemType Directory -Force -ErrorVariable err
                if($err)
                {
                    $log_string = $log_string + $err + "`r`n"
                    $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + "Creating Today folder was unsuccessful, break exporting $VM. Error: " + $err
                }
                else
                {
                    $log_string = $log_string + "Today folder was successfully created `r`n"

                    # If VM state is "Running"
                    if((Get-VMSummary $VM).enabledstate -eq "Running")
                    {
                        $log_string = $log_string + ((Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Stoping VM. `r`n")
                        invoke-vmshutdown -VM $VM -Reason "Export VM." -Force -ErrorVariable err
                        if($err)
                        {
                            $log_string = $log_string + $err + "`r`n"
                            $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Cann't stop $VM. Error: " + $err
                        }
                        else
                        {
                            $log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " VM was stopped. Starting export.`r`n"
                            $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " VM was stopped.`r`n"
                            $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Starting export $VM.`r`n"
                            export-VM -VM $VM -path ($backupDir + "\Today") -force -copystate -wait -ErrorVariable err
                            if($err)
                            {
                                $log_string = $log_string + $err + "`r`n"
                                $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Cann't finish export $VM. Error: " + $err
                            }
                            else
                            {
                                $log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " $VM successfully exported. Getting start.`r`n"
                                $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " $VM successfully exported.`r`n"
                                $export_count = $true
                            }
                            # Starting VM with 300 seconds pending                
                            Start-VM -vm $VM -HeartBeatTimeOut 300 -ErrorVariable err
                            if($err)
                            {
                                $log_string = $log_string + $err + "`r`n"
                                $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Cann't start $VM. Error: " + $err
                            }
                            else
                            {
                                $log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " $VM successfully started.`r`n"
                                $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " $VM successfully started.`r`n"
                            }
                        }
                    }  
                    elseif((Get-VMSummary $VM).enabledstate -eq "Stopped")
                    {
                        $log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " The state of $VM is Stopped. Starting export.`r`n"
                        $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " The state of $VM is Stopped.`r`n"
                        export-VM -VM $VM -path ($backupDir + "\Today") -force -copystate -wait -ErrorVariable err
                        if($err)
                        {
                            $log_string = $log_string + $err + "`r`n"
                            $MailBody = $Mailbody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Cann't finish export $VM. Error: " + $err
                        }
                        else
                        {
                            $log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " $VM successfully exported. Getting start.`r`n"
                            $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " $VM successfully exported.`r`n"
                            $export_count = $true
                        }
                    }
                    else
                    {
                        $vm_state = (Get-VMSummary -VM $VM).EnabledState
                        $log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Cann't export VM, because $VM = $vm_state.`r`n"
                        $MailBody = $MailBody + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Cann't export VM, because $VM = $vm_state.`r`n"
                    }
                }
            }
        }
    }
}
if($count) # If VM wasn't found.
{
    if(!($export_count)) # If VM does not exported
    {
        $log_string = $log_string + "Export $VM was finished with errors. Process terminated. `r`n"
        $MailBody = $MailBody + "Export $VM was finished with errors. Process terminated.`r`n"
    }
    else # If VM was exported successfully
    {
        $log_string = $log_string + "Searching *.VHD for $VM... `r`n"
        if(Get-Item -Path ($InfoDir + "\Today\" + $VM + "\Virtual Hard Disks\*") -Include *.*vhd | Select-Object -Property Name)
        {
            #$VHDInfo = "VHD Info:`r`n"
            foreach($element in (Get-Item -Path ($InfoDir + "\Today\" + $VM + "\Virtual Hard Disks\*") -Include *.*vhd | Select-Object -Property Name))
            {
                $log_string = $log_string + "Info for " + $element.Name + ":`r`n"
                $SomeVHDInfo = Get-VHDInfo -VHDPaths ($InfoDir + "\Today\" + $VM + "\Virtual Hard Disks\" + $element.Name)
                $log_string = $log_string + "Path: " + $SomeVHDInfo.Path + "`r`n"
                $log_string = $log_string + "Size: " + ([System.Math]::Round(($SomeVHDInfo.FileSize/1Gb),2)) + "Gb`r`n"
                $log_string = $log_string + "VM state: " + $SomeVHDInfo.InSavedState + "`r`n"
                $log_string = $log_string + "In Use: " + $SomeVHDInfo.InUse + "`r`n"
                $log_string = $log_string + "Max size: " + ([System.Math]::Round(($SomeVHDInfo.MaxInternalSize/1Gb),2)) + "Gb`r`n"
                $log_string = $log_string + "Type: " + $SomeVHDInfo.TypeName + "`r`n"

                $TestVHD = "VHD: " + $element.Name + ": " + (Test-VHD -VHDPaths ($InfoDir + "\Today\" + $VM + "\Virtual Hard Disks\" + $element.Name))
                $log_string = $log_string + $TestVHD + "`r`n"
                $MailBody = $Mailbody + $TestVHD + "`r`n"
            }
        }
        else # If VHD was not found
        {
            $log_string = $log_string + "*.VHD for $VM was not found.`r`n"
            $MailBody = $MailBody + "*.VHD for $VM was not found.`r`n"
        }
    }
}
else # VM was not found
{
    $log_string = $log_string + "$VM wasn't found. Process terminated.`r`n"
    $MailBody = $MailBody + "$VM wasn't found. Process terminated.`r`n"
}

$log_string = $log_string + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + " Stop working with $VM.`r`n"
$log_string = $log_string + "======================== `r`n"

$MailSubject = "$VM export report."
# Sending mail
SendEmail -Subject $MailSubject -Body $MailBody  -Priority $MailPriority

#net use R: /delete /y

 

Just change pathes and email addresses.

For russian-speakers: http://www.unix.ck.ua/content/eksport-virtualnykh-mashin-hyper-skript-v20