VMware Cloud Community
mark_chuman
Hot Shot
Hot Shot

Script to migrate cluster from one vCenter to another vCenter

Attached is a script I use to migrate an ESXi cluster from one vCenter to another.  I'll be updating the script (will add VM folder locations and attributes, more qa, root credential storage - PASSWORD IS STORED IN TRANSCRIPT FILE - so, adjust this in script or cleanse transcript after run), but for now it handles HA settings, DRS settings, EVC, DRS vm rules (affinity/anti), folder import/export, permissions (datacenter, cluster, host, folder and vm) and maybe some other things I left out.  Does not deal with dvs as we use local vswitches.

You'll need two VCs and a cluster you want to migrate.  The script does not create the datacenter in the destination VC, but it will create the cluster, disconnect, remove an add the hosts.

I hope it can help someone as I've mainly been on the receiving end with regards to help from this forum Smiley Happy.  Special note to Luc for a lot of the items in the script (folder functions, splat etc Smiley Happy).

Just unzip to a directory and run with powercli (requested credentials must have VC privs to disconnect hosts etc.).

As always with offerings, use at your own risk!

*Using to go from 5.0 to 5.5.

90 Replies
BrianOwen9
Contributor
Contributor

Mark, thank you for the quick reply with the script.

0 Kudos
rnotaro
Contributor
Contributor

Now im getting errors when I get to the import folders function

Please confirm your information before proceeding

Target cluster         - ****

Source vCenter         - ****

Source Datacenter      - *****

Destination vCenter    - *****

Destination datacenter - *****

Please confirm that the above information is correct and press any key to continue . . .

Running QA tests against the information provided

Cluster **** DOES exist in *****

Checking connection to the ESX hosts with the root password provided

*Script will halt if incorrect password is detected

Datacenter ***** DOES exist in *****

1

Exporting folders in source datacenter *****

Importing folders to destination datacenter *****

Confirm

Confirm

1/28/2015 10:47:00 AM Get-Folder  Folder with name '*******' was not found using the specified filter(s).

[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): a

get-folder : 1/28/2015 10:47:00 AM    Get-Folder        Folder with name 'Charlotte' was not found using the specified filter(s).

At C:\users\rnotaro\downloads\Migration Script\Migration Script\Functions\import_folder_function.ps1:28 char:61

+         $location = Get-Datacenter $dc | get-folder $type | get-folder $key

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

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

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

Action to take for this exception:

1/28/2015 10:47:04 AM New-Folder  Value cannot be found for the mandatory parameter Location

[C] Continue  [I] Silently Continue  [B] Break  [S] Suspend  [?] Help (default is "C"): c

New-Folder : 1/28/2015 10:47:04 AM    New-Folder        Value cannot be found for the mandatory parameter Location

At C:\users\rnotaro\downloads\Migration Script\Migration Script\Functions\import_folder_function.ps1:33 char:11

+           New-Folder -Name $folder.Name -Location $location

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

    + CategoryInfo          : NotSpecified: (:) [New-Folder], VimException

    + FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewFolder

0 Kudos
mark_chuman
Hot Shot
Hot Shot

Can you attach this file from the script run if it's safe to do so (company info)?

.\$SourcevCenter\$Cluster\folderexport.csv

0 Kudos
rnotaro
Contributor
Contributor

No but it looks like this.  it has 67 rows and 3 columns

NamePathType
cluster1DC\cluster1blue
cluster2DC\cluster2blue
0 Kudos
mark_chuman
Hot Shot
Hot Shot

Ok, trying to understand what makes your folder structure different.  Mine in testing was setup like this:

Name is the actual name of the folder.

Path is the path of the folder.  vm\ is the datacenter, next is Discovered virtual machine folder and sub folders underneath the Discovered virtual machine level folder.

Type is all blue or virtual machine folder.

NamePathType
Discovered virtual machinevm\Discovered virtual machineblue
Level1Cvm\Discovered virtual machine\Level1Cblue
Level1Bvm\Discovered virtual machine\Level1Bblue
Level1Avm\Discovered virtual machine\Level1Ablue
Level2Cvm\Discovered virtual machine\Level1C\Level2Cblue
Level2Bvm\Discovered virtual machine\Level1B\Level2Bblue
Level2Avm\Discovered virtual machine\Level1A\Level2A

blue

From what you provided it looks like the name field has names of clusters?  Where is the folder that threw an error located in the folder chain?  IE, is it a top-level folder where creation was attempted first?

0 Kudos
rnotaro
Contributor
Contributor

Cluster1 is just name of the folder that the vms are stored.   It happens to be the same name as the cluster.  Each cluster is its own client.  

All the folders are under the Datacenter object so DC\Cluster1, DC\Cluster2, etc

The error is on the datacenter name. Thats what it is saying it cant find.   Do I need to make a folder below the Datacenter object and put all my vm folders in that for the script to work?

0 Kudos
mark_chuman
Hot Shot
Hot Shot

You can try that, but first edit the import_folder_function.ps1 script and remove this error action handling part:

BEFORE

  foreach($folder in $VMfolder){

      $key = @()

      $key =  ($folder.Path -split "\\")[-2]

      if ($key -eq "vm") {

         get-datacenter $dc | get-folder $type | New-Folder -Name $folder.Name -ErrorAction SilentlyContinue

      } else

     {

AFTER

  foreach($folder in $VMfolder){

      $key = @()

      $key =  ($folder.Path -split "\\")[-2]

      if ($key -eq "vm") {

         get-datacenter $dc | get-folder $type | New-Folder -Name $folder.Name

      } else

     {

I'm thinking it is throwing an error before, but not exposing it.

0 Kudos
rnotaro
Contributor
Contributor

Looks the same with that piece removed.   

Confirm

1/28/2015 03:20:42 PM Get-Folder  Folder with name 'AS' was not found using the specified filter(s).

[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"):

get-folder : 1/28/2015 03:20:42 PM    Get-Folder        Folder with name 'AS' was not found using the specified filter(s).

At C:\users\rnotaro\downloads\Migration Script\Migration Script\Functions\import_folder_function.ps1:28 char:61

+         $location = Get-Datacenter $dc | get-folder $type | get-folder $key

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

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

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

Action to take for this exception:

1/28/2015 03:20:42 PM New-Folder  Value cannot be found for the mandatory parameter Location

[C] Continue  [I] Silently Continue  [B] Break  [S] Suspend  [?] Help (default is "C"):

New-Folder : 1/28/2015 03:20:42 PM    New-Folder        Value cannot be found for the mandatory parameter Location

At C:\users\rnotaro\downloads\Migration Script\Migration Script\Functions\import_folder_function.ps1:33 char:11

+           New-Folder -Name $folder.Name -Location $location

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

    + CategoryInfo          : NotSpecified: (:) [New-Folder], VimException

    + FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewFolder

0 Kudos
mark_chuman
Hot Shot
Hot Shot

Ok, I think I know what's going on.  The function sorts the csv it uses by the Path column.

Path
vm\Discovered virtual machine
vm\Discovered virtual machine\Level1C
vm\Discovered virtual machine\Level1B
vm\Discovered virtual machine\Level1A
vm\Discovered virtual machine\Level1C\Level2C
vm\Discovered virtual machine\Level1B\Level2B
vm\Discovered virtual machine\Level1A\Level2A

So, in my case the sort is actually aligning the folders in the order they will be created.  I think what is going on in your case is that when it does the sort it arranges the rows in a way that is causing the error - IE, the rows are not building on each other when it goes to import the folders back in.  Does this make sense and if so, take a look at your export csv and see if the logic holds true.  Basically, the first row (folder) must be implemented before the second one can be and so on.

For example, this would fail in my environment as the first level folder would not be created first.  I can't imagine right now exactly what folder layout would cause problems, but I'm thinking that is what's going on.

Path
vm\Discovered virtual machine\Level1A\Level2A
vm\Discovered virtual machine
vm\Discovered virtual machine\Level1C
vm\Discovered virtual machine\Level1B
vm\Discovered virtual machine\Level1A
vm\Discovered virtual machine\Level1C\Level2C
vm\Discovered virtual machine\Level1B\Level2B
0 Kudos
rnotaro
Contributor
Contributor

I noticed that import script sets the $type variable to "vm" but all of mine in my CSV are type "blue"   Not sure if thats part of the problem.

0 Kudos
rnotaro
Contributor
Contributor

I noticed you dont have a datacenter in your path so the script must not be pulling that in for you but for me it is

So lets say my first folder name is Red and my datacenter name is Colors

The the first line in my export csv is

Name               Path                    type

Red                  Colors\Red          blue

When it goes to import it back into the new VC there is no Colors datacenter obviously so is that why its failing?

0 Kudos
mark_chuman
Hot Shot
Hot Shot

Interesting, so the first record in your csv in the path column is Colors?  Like this? - ignore the other items in the path, just keying on the first one.

Path

Colors\Discovered virtual machine\Level1A\Level2A

Colors\Discovered virtual machine

Colors\Discovered virtual machine\Level1C

Colors\Discovered virtual machine\Level1B

Colors\Discovered virtual machine\Level1A

Colors\Discovered virtual machine\Level1C\Level2C

Colors\Discovered virtual machine\Level1B\Level2B

If this is the case you may want to try to edit this portion of the import_folder_function.ps1

BEFORE

#Loop to import folders.  Objects are sorted by path field in above import.

#Splits records by slashes and sets key as record just prior to last record. 

#The if loop then creates the first folder and then the next does all the rest

# of the folders.  The entire loop will fail without the first creation of the "vm" folder.

#Error action on first folder creation is set as this first folder, Discovered virtual machine folder will trigger errors after the first script run on a datacenter.

  foreach($folder in $VMfolder){

      $key = @()

      $key =  ($folder.Path -split "\\")[-2]

      if ($key -eq "vm") {

         get-datacenter $dc | get-folder $type | New-Folder -Name $folder.Name -ErrorAction SilentlyContinue

      } else

     {

        $location = Get-Datacenter $dc | get-folder $type | get-folder $key

        Try{

          Get-Folder -Name $folder.Name -Location $location -ErrorAction Stop

        }

        Catch{

          New-Folder -Name $folder.Name -Location $location

        }

      }

   }

}

AFTER (Only adjusting the initial key scan to Colors instead of "vm".)

#Loop to import folders.  Objects are sorted by path field in above import.

#Splits records by slashes and sets key as record just prior to last record. 

#The if loop then creates the first folder and then the next does all the rest

# of the folders.  The entire loop will fail without the first creation of the "vm" folder.

#Error action on first folder creation is set as this first folder, Discovered virtual machine folder will trigger errors after the first script run on a datacenter.

  foreach($folder in $VMfolder){

      $key = @()

      $key =  ($folder.Path -split "\\")[-2]

      if ($key -eq "Colors") {

         get-datacenter $dc | get-folder $type | New-Folder -Name $folder.Name -ErrorAction SilentlyContinue

      } else

     {

        $location = Get-Datacenter $dc | get-folder $type | get-folder $key

        Try{

          Get-Folder -Name $folder.Name -Location $location -ErrorAction Stop

        }

        Catch{

          New-Folder -Name $folder.Name -Location $location

        }

      }

   }

}

0 Kudos
mark_chuman
Hot Shot
Hot Shot

Feel free!  On my list, but lower priority as we don't use them Smiley Sad.

0 Kudos
rnotaro
Contributor
Contributor

Ok that fixed that issue! 

Now when it gets down to the part where it adds the hosts to the new vCenter it fails again.

Finished removing.  Disconnecting from the source vCenter  BLAH

Adding the hosts into tne new cluster CLUSTER on BLAH2

Confirm

1/29/2015 09:19:46 AM Add-VMHost  The specified parameter 'Location' expects a single value, but your name criteria 'CLUSTER' corresponds to multiple values.

[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): y

Add-VMHost : 1/29/2015 09:19:46 AM    Add-VMHost        The specified parameter 'Location' expects a single value, but your name criteria 'CLUSTER' corresponds

to multiple values.

At C:\users\rnotaro\downloads\Migration Script\Migration Script\FROMCSV_vCenterMigration.ps1:532 char:33

+ foreach ($vmhost in $CSVhosts) {Add-VMHost $vmhost.name -Location $Cluster -User ...

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

    + CategoryInfo          : InvalidResult: (System.Collecti...dObjectInterop]:List`1) [Add-VMHost], VimException

    + FullyQualifiedErrorId : Core_ObnSelector_SelectObjectByNameCore_MoreResultsThanExpected,VMware.VimAutomation.ViCore.Cmdlets.Commands.AddVMHost

Confirm

1/29/2015 09:20:08 AM Add-VMHost  VIContainer parameter: Could not find any of the objects specified by name.

[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): y

Add-VMHost : 1/29/2015 09:20:08 AM    Add-VMHost        VIContainer parameter: Could not find any of the objects specified by name.

At C:\users\rnotaro\downloads\Migration Script\Migration Script\FROMCSV_vCenterMigration.ps1:532 char:33

+ foreach ($vmhost in $CSVhosts) {Add-VMHost $vmhost.name -Location $Cluster -User ...

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

    + CategoryInfo          : ObjectNotFound: (VMware.VimAutom...tainer Location:RuntimePropertyInfo) [Add-VMHost], ObnRecordProcessingFailedException

    + FullyQualifiedErrorId : Core_ObnSelector_SetNewParameterValue_ObjectNotFoundCritical,VMware.VimAutomation.ViCore.Cmdlets.Commands.AddVMHost

Confirm

1/29/2015 09:20:28 AM Add-VMHost  The specified parameter 'Location' expects a single value, but your name criteria 'CLUSTER' corresponds to multiple values.

[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): a

Add-VMHost : 1/29/2015 09:20:28 AM    Add-VMHost        The specified parameter 'Location' expects a single value, but your name criteria 'CLUSTER' corresponds

to multiple values.

At C:\users\rnotaro\downloads\Migration Script\Migration Script\FROMCSV_vCenterMigration.ps1:532 char:33

+ foreach ($vmhost in $CSVhosts) {Add-VMHost $vmhost.name -Location $Cluster -User ...

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

    + CategoryInfo          : InvalidResult: (System.Collecti...dObjectInterop]:List`1) [Add-VMHost], VimException

    + FullyQualifiedErrorId : Core_ObnSelector_SelectObjectByNameCore_MoreResultsThanExpected,VMware.VimAutomation.ViCore.Cmdlets.Commands.AddVMHost

Add-VMHost : 1/29/2015 09:20:46 AM    Add-VMHost        VIContainer parameter: Could not find any of the objects specified by name.

At C:\users\rnotaro\downloads\Migration Script\Migration Script\FROMCSV_vCenterMigration.ps1:532 char:33

+ foreach ($vmhost in $CSVhosts) {Add-VMHost $vmhost.name -Location $Cluster -User ...

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

    + CategoryInfo          : ObjectNotFound: (VMware.VimAutom...tainer Location:RuntimePropertyInfo) [Add-VMHost], ObnRecordProcessingFailedException

    + FullyQualifiedErrorId : Core_ObnSelector_SetNewParameterValue_ObjectNotFoundCritical,VMware.VimAutomation.ViCore.Cmdlets.Commands.AddVMHost

0 Kudos
mark_chuman
Hot Shot
Hot Shot

Can you please try this?

Close all powercli sessions.  Launch a new powercli session and do not connect to any vCenter servers.  Run this to confirm - disconnect-viserver * -confirm:$false

Now try and run the script.  I think that error is being caused by another vCenter session being open.

*When I kill the script mid-run I always perform a quick clean up by running these two commands:

stop-transcript

and

disconnect-viserver * -confirm:$false

0 Kudos
rnotaro
Contributor
Contributor

Ok I did that and ran the command to disconnect vcenter.  Same error.

I manually added the hosts so the script would continue and it gives a similar error later in the script. 

Importing permissions for the folders, hosts, virtual machines, datacenter and cluster

Importing DRS rules (if any were exported)

Importing VM folder locations

Confirm

1/29/2015 11:06:33 AM Move-VM  The specified parameter 'Destination' expects a single value, but your name criteria 'CLUSTER' corresponds to multiple values.

[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"): y

Move-VM : 1/29/2015 11:06:33 AM    Move-VM        The specified parameter 'Destination' expects a single value, but your name criteria 'CLUSTER' corresponds

to multiple values.

At C:\users\rnotaro\Downloads\Migration Script\Migration Script\FROMCSV_vCenterMigration.ps1:662 char:105

+ ... ne") { } else {Move-VM -VM $vm.Name -Destination $vm.Folder -RunAsync}}

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

    + CategoryInfo          : InvalidResult: (System.Collecti...dObjectInterop]:List`1) [Move-VM], VimException

    + FullyQualifiedErrorId : Core_ObnSelector_SelectObjectByNameCore_MoreResultsThanExpected,VMware.VimAutomation.ViCore.Cmdlets.Commands.MoveVM

0 Kudos
rnotaro
Contributor
Contributor

ok, I believe the issue is that the cluster name and folder name are the same and thats where the multiple names are coming from.  I renamed my folder to be different from the cluster name and the script finished!   Maybe there's a way to identtfy the folder or cluster by id instead of name?

0 Kudos
mark_chuman
Hot Shot
Hot Shot

So, one thing I am finding is that the import portion of the script does not like:

1.     Duplicate folder names that exist under the same vCenter - ie, the same folder name existing say in different datacenters under the same VC.

2.     VMs that are placed in the "vm" folder as this is a hidden type folder and it chokes when it tries to import them in.

Working now to update the script for this - may be related to what you are seeing.  We've used the script hundreds of times on more simple clusters as far as layouts go and we are moving to more complex environments (larger folder trees, complex provisioning/placement), so that's why I haven't see this error before.

0 Kudos
mark_chuman
Hot Shot
Hot Shot

I'll update the script, but I added this tidbit to narrow down the scope to the datacenter you are working with.

#Import folder permissions.  Will be executed at operator's discretion.

#Note that the $folder value is narrowed down to the destinationdatacenter as a duplicate folder in another datacenter will cause an error.

$folderperms = Import-Csv "./$SourcevCenter/$Cluster./perms_export_folders.csv"

foreach ($fpm in $folderperms) {

  $svcgroup = $fpm.Principal

  $folder = Get-Datacenter $DestinationDatacenter | Get-Folder -Name $fpm.Entity

  $authMgr = Get-View AuthorizationManager

  $perm = New-Object VMware.Vim.Permission

  $perm.principal = $svcgroup

  $perm.group = if ($fpm.IsGroup -eq "TRUE") {$true} else {$null}

  $perm.propagate = if ($fpm.Propagate -eq "TRUE") {$true} else {$null}

  $perm.roleid = ($authMgr.RoleList | where{$_.Name -eq $fpm.Role}).RoleId

  $authMgr.SetEntityPermissions(($folder | Get-View).MoRef, $perm)

Trying to figure out how to narrow this down now:

#Begin import of virtual machine folder locations

$VMFolderLocationImport = Import-Csv .\folderlocation_export_vms.csv

Write-Host " "

Write-Host "Importing" -ForegroundColor Green -NoNewline

Write-Host " VM folder locations"

foreach ($vm in $VMFolderLocationImport) { If ($vm.Folder -Like "Discovered virtual machine") { } else {Move-VM -VM $vm.Name -Destination $vm.Folder -RunAsync}}

#End import of virtual machine folder locations

0 Kudos
mark_chuman
Hot Shot
Hot Shot

Here is the updated version, but still working on this portion:

#Begin import of virtual machine folder locations

$VMFolderLocationImport = Import-Csv .\folderlocation_export_vms.csv

Write-Host " "

Write-Host "Importing" -ForegroundColor Green -NoNewline

Write-Host " VM folder locations"

foreach ($vm in $VMFolderLocationImport) { If ($vm.Folder -Like "Discovered virtual machine") { } else {Move-VM -VM $vm.Name -Destination $vm.Folder -RunAsync}}

#End import of virtual machine folder locations

So, if the folder the script is trying to move the VM back to is not unique within the vCenter (ie, the folder name exists under two or more datacenters within your destination vCenter) it will error out.

0 Kudos