VMware Cloud Community
emcclure
Enthusiast
Enthusiast
Jump to solution

Is there a way to convert a template to a VM, migrate it and then mark it back as a template in a script?

Hello,

I've written a script that migrates VM's from host to host serially as per the requirements of the person who needed it.  After the migration is done the host is put in maintenance mode so it can be updated.  I found that I needed to add an ability to move templates, as it doesn't seem to move them when the host is put into maintenance mode.  As of the moment if the host doesn't have templates it will migrate all the VM's, but not move to putting the host in maintenance mode.  If it has templates it will convert them and move them, but not set them back as templates.  It will then put the host into maintenance mode though.  Here's an excerpt from my script:

$cluster = Get-Datacenter -Name $dc[$answer - 1].Name | Get-Cluster | sort Name | Out-GridView -Title 'Select a cluster' -OutputMode Single #Gets the clusters in the datacenter
$mycluster = Get-Cluster $cluster.Name
$host1 = Get-Cluster "$mycluster" | Get-VMHost | where {$_.ConnectionState -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select the host to migrate VMs from' -OutputMode Single #Lists all hosts in Connected mode in the cluster
$fromhost = Get-VMHost $host1.Name
$host2 = Get-Cluster "$mycluster" | Get-VMHost | where {$_.ConnectionState -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select the host to migrate VMs to' -OutputMode Single #Lists all hosts in Connected mode in the cluster
$tohost = Get-VMHost $host2.Name
$selectedtemplates = Get-Template -Location $fromhost

$selectedvms = Get-VM -Location $fromhost #| sort Name | #Out-GridView -Title 'Select the VMs you wish to migrate' -OutputMode Multiple #Opens a window allowing you to click on the VM's you want to migrate

if($selectedvms){
foreach ($vm in $selectedvms)
{
Write-Host "Relocating VM:" $vm.Name "to" $tohost
Get-VM -Name $vm.Name | Move-VM -Destination $tohost -diskstorageformat thin #Moves to the new host one at a time
}

if($selectedtemplates.Count -gt 0) {
foreach ($template in $selectedtemplates)
{
  Write-Host "Relocation Template:" $template.Name "to" $tohost
  Set-Template -Template $template.Name -ToVM
  Get-VM -Name $template.Name | Move-VM -Destination $tohost -diskstorageformat thin
  Get-VM -$template.Name | Set-VM ToTemplate
 

Set-VMHost -VMHost $fromhost -State "Maintenance"

Sorry I don't know how to copy the code on here to make it look all pretty.  Thoughts anybody?

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Something like this?

$allHost = ''

while('y','n' -notcontains $allHost){

    $allHost = Read-Host -Prompt "Stand-alone ESXI nodes? (y/n)"

}

if($allHost -eq 'n'){

# Select ESXi nodes from cluster

    $cluster = Get-Datacenter -Name $dc[$answer - 1].Name | Get-Cluster | Sort-Object -Property Name

    $clusterSelected = $cluster | Out-GridView -Title 'Select a cluster' -OutputMode Single

   

    $esx = Get-VMHost -Location $clusterSelected | where {$_.ConnectionState -eq "Connected"} | Sort-Object -Property Name

}

else{

# Select all stand-alone ESXi nodes

    $esx = Get-VMHost | where{-not (Get-Cluster -VMHost $_)} |

        where {$_.ConnectionState -eq "Connected"} | Sort-Object -Property Name

}

# Pick from ESXi node

$esxFrom = $esx | Out-GridView -Title 'Select the host to migrate VMs from' -OutputMode Single

# Remove from ESXi node from list

$esx = Get-VMHost -Location $clusterSelected | where {$_.ConnectionState -eq "Connected" -and $_.Name -ne $esxFrom.Name} |

    Sort-Object -Property Name

# Pick to ESXi node

$esxTo = $esx | Out-GridView -Title 'Select the host to migrate VMs to' -OutputMode Single

# Templates to VM (register on $esxTo)

$selectedtemplates = Get-Template -Location $esxFrom |

    Set-Template -ToVM -Confirm:$false | Move-VM -Destination $esxTo -Confirm:$false

$selectedtemplates | select Name |

    Export-CSV -Path .\templates.csv -NoTypeInformation -UseCulture

# Convert back to Templates

Import-Csv -Path .\templates.csv -UseCulture | Foreach-Object -Process {

    Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false

}

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

0 Kudos
19 Replies
LucD
Leadership
Leadership
Jump to solution

Why don't you, before placing the ESXi node in maintenance mode, convert all templates on that ESXi node to VMs?

Then your normal procedure to place the ESXi node in maintenance mode should move those VMs also to another ESXi node.

You can save the names templates you converted to a VM on an ESXi node to an external file, and once moved convert them back to templates.

To convert all templates on a specific ESXi node, you can do

$esxName = 'MyEsx'

$esx = Get-VMHost -Name $esxName

Get-Template -Location $esx | Set-Template -ToVM -Confirm:$false |

select Name | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

Once all the VMs are evacuated from the ESXi node, you can convert them back to Templates.

Something like this

Import-Csv -Path .\report.csv -UseCulture  |

ForEach-Object -Process {

    Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Ok that makes more sense.  So I have this now for the code:

$cluster = Get-Datacenter -Name $dc[$answer - 1].Name | Get-Cluster | sort Name | Out-GridView -Title 'Select a cluster' -OutputMode Single #Gets the clusters in the datacenter
$mycluster = Get-Cluster $cluster.Name
$host1 = Get-Cluster "$mycluster" | Get-VMHost | where {$_.ConnectionState -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select the host to migrate VMs from' -OutputMode Single #Lists all hosts in Connected mode in the cluster
$fromhost = Get-VMHost $host1.Name
$host2 = Get-Cluster "$mycluster" | Get-VMHost | where {$_.ConnectionState -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select the host to migrate VMs to' -OutputMode Single #Lists all hosts in Connected mode in the cluster
$tohost = Get-VMHost $host2.Name
$selectedtemplates = Get-Template -Location $fromhost | Set-Template -ToVM -Confirm:$false | select Name | Export-CSV -Path .\templates.csv -NoTypeInformation -UseCulture

$selectedvms = Get-VM -Location $fromhost #| sort Name | #Out-GridView -Title 'Select the VMs you wish to migrate' -OutputMode Multiple #Opens a window allowing you to click on the VM's you want to migrate

if($selectedvms){
foreach ($vm in $selectedvms)
{
Write-Host "Relocating VM:" $vm.Name "to" $tohost
Get-VM -Name $vm.Name | Move-VM -Destination $tohost -diskstorageformat thin #Moves to the new host one at a time
Sleep 10
Import-Csv -Path .\templates.csv -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false }
}

And it converts the templates perfectly and migrates the VM's on the host.  For some reason though it gives me an error on the templates:

Get-VM : 11/16/2018 2:47:37 PM  Get-VM          VM with name 'vegasupdtest' was not found using the specified filter(s).

At C:\Users\emcclure\Desktop\GenScripts\GenSeriallyMigrateFromHostToHost60.ps1:73 char:75

+ ...  -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name | Set-V ...

+                                              ~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (:) [Get-VM], VimException

    + FullyQualifiedErrorId : Core_OutputHelper_WriteNotFoundError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM.

Line 73 was this: Import-Csv -Path .\templates.csv -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false }

0 Kudos
LucD
Leadership
Leadership
Jump to solution

And is there a VM with that name present in the environment?

What does the CSV file look like?
It should say

Name

VM1

VM2

Is the Move-VM completed before you try to convert the VM back to a Template?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

CSV looks like what you show.

Odd.  So I selected the hosts to migrate from and to.  The from host had about 7 templates and 2 VM's.  VM's migrated to the new host without issue.  But the templates behaved differently.  For whatever reason they converted to a VM, migrated to some other random host in the cluster and then converted back to the template.  However it then seems to try again to convert them to a template, but can't for whatever reason and throws up an error saying it can't find the VM when they're actually templates again.  I'm not sure why that's happening or why it's moving them to random hosts in the cluster.

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Another problem I just noticed is when a host doesn't have VM's but only templates.  It allows me to do the initial steps, but when it comes to the migration it just stops, never bothers to convert the template to a VM and move it.

Right now I'm testing it on hosts that have both vm's and templates.  I can get the script to not error, however for whatever reason with the templates it puts them on a random host still instead of putting them where I want them to go.  Still not understanding this.  Here's the current code:

$cluster = Get-Datacenter -Name $dc[$answer - 1].Name | Get-Cluster | sort Name | Out-GridView -Title 'Select a cluster' -OutputMode Single #Gets the clusters in the datacenter
$mycluster = Get-Cluster $cluster.Name
$host1 = Get-Cluster "$mycluster" | Get-VMHost | where {$_.ConnectionState -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select the host to migrate VMs from' -OutputMode Single #Lists all hosts in Connected mode in the cluster
$fromhost = Get-VMHost $host1.Name
$host2 = Get-Cluster "$mycluster" | Get-VMHost | where {$_.ConnectionState -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select the host to migrate VMs to' -OutputMode Single #Lists all hosts in Connected mode in the cluster
$tohost = Get-VMHost $host2.Name
$selectedtemplates = Get-Template -Location $fromhost | Set-Template -ToVM -Confirm:$false | select Name | Export-CSV -Path .\templates.csv -NoTypeInformation -UseCulture

$selectedvms = Get-VM -Location $fromhost

if($selectedvms){
foreach ($vm in $selectedvms)
{
Write-Host "Relocating VM:" $vm.Name "to" $tohost
Get-VM -Name $vm.Name | Move-VM -Destination $tohost -diskstorageformat thin #Moves to the new host one at a time

}

Import-Csv -Path .\templates.csv -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false }

Set-VMHost -VMHost $fromhost -State "Maintenance"

0 Kudos
LucD
Leadership
Leadership
Jump to solution

I think the issue with the script might be that when you convert a Template to a VM, the VM is registered through RDS on any ESXi node in the cluster.

Since the Templates are most probably stored on shared storage in the cluster, this has indeed no impact.

But for the script it would mean that the VM (ex-Template) might now be registered on another ESXi node then were it was before the conversion.

The script will need to keep that into account when moving the ex-Templates.

In fact, come to think of it, with the shared storage in the cluster, there isn't really a need to "move" the ex-Templates, it's sufficient to register them on another ESXi node.

I'll see how the script can be adapted accordingly.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
LucD
Leadership
Leadership
Jump to solution

I have rewritten the script a bit.

See of this works better.

# Select cluster

$cluster = Get-Datacenter -Name $dc[$answer - 1].Name | Get-Cluster | Sort-Object -Property Name

$clusterSelected = $cluster | Out-GridView -Title 'Select a cluster' -OutputMode Single

$esx = Get-VMHost -Location $clusterSelected | where {$_.ConnectionState -eq "Connected"} | Sort-Object -Property Name

# Pick from ESXi node

$esxFrom = $esx | Out-GridView -Title 'Select the host to migrate VMs from' -OutputMode Single

# Remove from ESXi node from list

$esx = Get-VMHost -Location $clusterSelected | where {$_.ConnectionState -eq "Connected" -and $_.Name -ne $esxFrom.Name} |

    Sort-Object -Property Name

# Pick to ESXi node

$esxTo = $esx | Out-GridView -Title 'Select the host to migrate VMs to' -OutputMode Single

# Templates to VM (register on $esxTo)

$selectedtemplates = Get-Template -Location $esxFrom |

    Set-Template -ToVM -Confirm:$false | Move-VM -Destination $esxTo -Confirm:$false

$selectedtemplates | select Name |

    Export-CSV -Path .\templates.csv -NoTypeInformation -UseCulture

# Convert back to Templates

Import-Csv -Path .\templates.csv -UseCulture | Foreach-Object -Process {

    Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false

}

# VMs

Get-VM -Location $esxFrom |

ForEach-Object -Process {

    Write-Host "Relocating VM:" $vm.Name "to" $tohost

    Move-VM -VM $_ -Destination $esxTo -DiskStorageFormat Thin

}

# Set ESXi node in maintenance mode ... and wait till done

Set-VMHost -VMHost $esxFrom -State Maintenance

while($esxFrom.State -ne 'Maintenance'){

    sleep 5

    $esxFrom = Get-VMHost -Name $esxFrom

}

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Hi LucD,

Sorry for the delay in response as I was on vacation.  I've copied your script, but when I get to the part where it lists the cluster it just lists them all under one line.  If I click ok then it lists all the hosts the same way.  I have attached a copy of what the output looks like.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

My bad, the InputObject parameter on Out-GridView doesn't work as I intended.

It does work when you pipe the objects to Out-GridView.

I updated the code above accordingly.
Give it another try.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Hi LucD,

Working a lot better now.  One last thing I noticed is that in this part of the script:

Get-VM -Location $esxFrom |

ForEach-Object -Process {

    Write-Host "Relocating VM:" $vm.Name "to" $esxTo

It's not putting the VM name in there.  It only puts the host it's being migrated to.

Edit: Please disregard.  I added a line from a different version of the script to fix it.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

That line should say

    Write-Host "Relocating VM:" $_.Name "to" $tohost


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

The $tohost wasn't being used anywhere else in the script, so once I changed it to $esxTo then that part worked.

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Is there an easy way to change the script as well to determine if you are going to use a host or cluster setup?  We do have some hosts that aren't in clusters for certain reasons, but they do have common datastores, so it might be nice to add that functionality just in case, or maybe it's just best to write another script that just ignores clusters altogether?

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Something like this?

$allHost = ''

while('y','n' -notcontains $allHost){

    $allHost = Read-Host -Prompt "Stand-alone ESXI nodes? (y/n)"

}

if($allHost -eq 'n'){

# Select ESXi nodes from cluster

    $cluster = Get-Datacenter -Name $dc[$answer - 1].Name | Get-Cluster | Sort-Object -Property Name

    $clusterSelected = $cluster | Out-GridView -Title 'Select a cluster' -OutputMode Single

   

    $esx = Get-VMHost -Location $clusterSelected | where {$_.ConnectionState -eq "Connected"} | Sort-Object -Property Name

}

else{

# Select all stand-alone ESXi nodes

    $esx = Get-VMHost | where{-not (Get-Cluster -VMHost $_)} |

        where {$_.ConnectionState -eq "Connected"} | Sort-Object -Property Name

}

# Pick from ESXi node

$esxFrom = $esx | Out-GridView -Title 'Select the host to migrate VMs from' -OutputMode Single

# Remove from ESXi node from list

$esx = Get-VMHost -Location $clusterSelected | where {$_.ConnectionState -eq "Connected" -and $_.Name -ne $esxFrom.Name} |

    Sort-Object -Property Name

# Pick to ESXi node

$esxTo = $esx | Out-GridView -Title 'Select the host to migrate VMs to' -OutputMode Single

# Templates to VM (register on $esxTo)

$selectedtemplates = Get-Template -Location $esxFrom |

    Set-Template -ToVM -Confirm:$false | Move-VM -Destination $esxTo -Confirm:$false

$selectedtemplates | select Name |

    Export-CSV -Path .\templates.csv -NoTypeInformation -UseCulture

# Convert back to Templates

Import-Csv -Path .\templates.csv -UseCulture | Foreach-Object -Process {

    Get-VM -Name $_.Name | Set-VM -ToTemplate -Confirm:$false

}

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

LucD,

That works.  Thanks so much for your help on this.

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Just had someone try this on vCenter 6.0 and got this error:

Move-VM : 11/26/2018 12:11:49 PM        Move-VM         The operation for the entity "vmname" failed with the following

message: "Virtual machine is configured to use a device that prevents the operation: Device 'HD audio ' has a backing

type that is not supported. This is a general limitation of the virtual machine's compatibility with the ESXi version

of the selected host."

At C:\Users\admin\Desktop\GenSeriallyMigrateFromHostToHostv2.ps1:99 char:5

+     Move-VM -VM $_ -Destination $esxTo -DiskStorageFormat Thin

+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [Move-VM], DisallowedMigrationDeviceAttached

    + FullyQualifiedErrorId : Client20_TaskServiceImpl_CheckServerSideTaskUpdates_OperationFailed,VMware.VimAutomation

   .ViCore.Cmdlets.Commands.MoveVM

Script failed, wouldn't migrate the VM's.  From what I understand the VM HW version is 9.  Should it be upgraded?  As far as I know there shouldn't be any issue to upgrade the HW version, but I don't know 100% for sure on that.

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

And another thing that was just noticed was that a VM name was being repeated in the PowerCLI window as being migrated when it had already been migrated.  It was working on a new VM, but for some reason this part: Write-Host "Relocating VM:" $vm.Name "to" $esxTo kept repeating the same VM name.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Looks like there were some extra lines of code in the last script I posted, that didn't need to be there.

Don't know how that happened.

I updated the last version of the script.

Not sure how you got a VM with an audio device on there.
In any case, when that kind of device is connected, you can't vMotion the VM.

The only solution afaik, is to remove the device before the vMotion.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Good to know.  Is there a way to detect if an audio device is connected to a VM somehow?  I'm poking around a little bit, but don't see anything that's very obvious.  Or perhaps there's a command that detects if anything connected to a VM would block a vMotion?

Edit: Never mind.  I found this thread Remove all "HD Audio" devices after P2V? But it looks like the VM needs to be powered off anyway to remove any audio device, and that might not always be an option.

0 Kudos