2010-01-20

vCenter PowerCLI Migration Script

As I promised in a previous post I wanted to migrate from a vCenter to whole new machine

Process was:

  • Export folders
  • Export VM locations in Folders
  • Export Permissions
  • Export Custom Attributes
  • Create Folders on the new vCenter
  • Disable DRS/HA
  • Remove ESX hosts from Source vCenter and add to Destination vCenter
  • Enable DRS/HA again
  • Move all vm’s to correct folders
  • Apply the permissions back
  • Apply custom attributes and notes

Here is the script

   1: #load Vmware Module
   2: Add-PSSnapin VMware.VimAutomation.Core
   3:  
   4: #Change to multi-mode vcenter management
   5: Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Confirm:$false
   6:  
   7: #Get vCenter Server Names
   8: $sourceVI = Read-Host "Please enter the name of the source Server"; 
   9: $destVI = Read-Host "Please enter the name of the destination Server"
  10:  
  11: $creds = get-credential
  12:  
  13: $datacenter = Read-Host "Please give the name of the datacenter you would like to run against"
  14:  
  15:  
  16: #Connect to Source vCenter
  17: connect-viserver -server $sourceVI -credential $creds
  18: connect-viserver -server $destVI -credential $creds -NotDefault:$false
  19:  
  20:  
  21: filter Get-FolderPath {
  22:     $_ | Get-View | % {
  23:         $row = "" | select Name, Path
  24:         $row.Name = $_.Name
  25:  
  26:         $current = Get-View $_.Parent
  27:         $path = $_.Name
  28:         do {
  29:             $parent = $current
  30:             if($parent.Name -ne "vm"){$path = $parent.Name + "\" + $path}
  31:             $current = Get-View $current.Parent
  32:         } while ($current.Parent -ne $null)
  33:         $row.Path = $path
  34:         $row
  35:     }
  36: }
  37:  
  38: ## Export all folders
  39: $report = @()
  40: $report = get-datacenter $datacenter -Server $sourceVI| Get-folder vm | get-folder | Get-Folderpath
  41:         ##Replace the top level with vm
  42:         foreach ($line in $report) {
  43:         $line.Path = ($line.Path).Replace($datacenter + "\","vm\")
  44:         }
  45: $report | Export-Csv "c:\Folders-with-FolderPath-$($datacenter).csv" -NoTypeInformation
  46:  
  47: ##Export all VM locations
  48: $report = @()
  49: $report = get-datacenter $datacenter -Server $sourceVI| get-vm | Get-Folderpath
  50:  
  51: $report | Export-Csv "c:\vms-with-FolderPath-$($datacenter).csv" -NoTypeInformation
  52:  
  53:  
  54: #Get the Permissions  
  55:  
  56: $folderperms = get-datacenter $datacenter -Server $sourceVI | Get-Folder | Get-VIPermission
  57: $vmperms = Get-Datacenter $datacenter -Server $sourceVI | get-vm | Get-VIPermission
  58:  
  59: $permissions = get-datacenter $datacenter -Server $sourceVI | Get-VIpermission
  60:         
  61:         $report = @()
  62:               foreach($perm in $permissions){
  63:                 $row = "" | select EntityId, FolderName, Role, Principal, IsGroup, Propagate
  64:                 $row.EntityId = $perm.EntityId
  65:                 $Foldername = (Get-View -id $perm.EntityId).Name
  66:                 $row.FolderName = $foldername
  67:                 $row.Principal = $perm.Principal
  68:                 $row.Role = $perm.Role
  69:                 $row.IsGroup = $perm.IsGroup
  70:                 $row.Propagate = $perm.Propagate
  71:                 $report += $row
  72:             }
  73:     
  74:             foreach($perm in $folderperms){
  75:                 $row = "" | select EntityId, FolderName, Role, Principal, IsGroup, Propagate
  76:                 $row.EntityId = $perm.EntityId
  77:                 $Foldername = (Get-View -id $perm.EntityId).Name
  78:                 $row.FolderName = $foldername
  79:                 $row.Principal = $perm.Principal
  80:                 $row.Role = $perm.Role
  81:                 $row.IsGroup = $perm.IsGroup
  82:                 $row.Propagate = $perm.Propagate
  83:                 $report += $row
  84:             }
  85:  
  86:             foreach($perm in $vmperms){
  87:                 $row = "" | select EntityId, FolderName, Role, Principal, IsGroup, Propagate
  88:                 $row.EntityId = $perm.EntityId
  89:                 $Foldername = (Get-View -id $perm.EntityId).Name
  90:                 $row.FolderName = $foldername
  91:                 $row.Principal = $perm.Principal
  92:                 $row.Role = $perm.Role
  93:                 $row.IsGroup = $perm.IsGroup
  94:                 $row.Propagate = $perm.Propagate
  95:                 $report += $row
  96:             }
  97:  
  98:         $report | export-csv "c:\perms-$($datacenter).csv" -NoTypeInformation
  99:  
 100: ##Export VM Custom Attributes and notes
 101:  
 102: $vmlist = get-datacenter $datacenter -Server $sourceVI| get-vm 
 103: $Report =@()
 104:     foreach ($vm in $vmlist) {
 105:         $row = "" | Select Name, Notes, Key, Value, Key1, Value1
 106:         $row.name = $vm.Name
 107:         $row.Notes = $vm | select -ExpandProperty Notes
 108:         $customattribs = $vm | select -ExpandProperty CustomFields
 109:         $row.Key = $customattribs[0].Key
 110:         $row.Value = $customattribs[0].value
 111:         $row.Key1 = $customattribs[1].Key
 112:         $row.Value1 = $customattribs[1].value    
 113:         $Report += $row
 114:     }
 115:  
 116: $report | Export-Csv "c:\vms-with-notes-and-attributes-$($datacenter).csv" -NoTypeInformation
 117:    
 118:     
 119: ##Disconnect-VIServer -Server $sourceVI -force -confirm:$false
 120:  
 121:  
 122: #connect to Destination Server
 123: ##connect-viserver -server $destVI -credential $creds -confirm:$false
 124:  
 125:  
 126: ##IMPORT FOLDERS
 127: $vmfolder = Import-Csv "c:\Folders-with-FolderPath-$($datacenter).csv" | Sort-Object -Property Path
 128:  
 129: foreach($folder in $VMfolder){
 130:     $key = @()
 131:     $key =  ($folder.Path -split "\\")[-2]
 132:     if ($key -eq "vm") {
 133:         get-datacenter $datacenter -Server $destVI | get-folder vm | New-Folder -Name $folder.Name
 134:         } else {
 135:         get-datacenter $datacenter -Server $destVI | get-folder vm | get-folder $key | New-Folder -Name $folder.Name 
 136:         }
 137: }
 138:  
 139: ##ESX host migration
 140:  
 141: #Switch off HA
 142: Get-Cluster $datacenter -Server $sourceVI  | Set-Cluster -HAEnabled:$false -DrsEnabled:$false -Confirm:$false
 143:  
 144: #Remove ESX hosts from old vcenter
 145: $Myvmhosts = get-datacenter $datacenter -Server $sourceVI | Get-VMHost 
 146: foreach ($line in $Myvmhosts) {
 147: Get-vmhost -Server $sourceVI -Name $line.Name | Set-VMHost -State "Disconnected" -Confirm:$false
 148: Get-VMHost -server $sourceVI -Name $line.Name | Remove-VMHost -Confirm:$false
 149: }
 150: #add ESX hosts into new vcenter
 151: foreach ($line in $Myvmhosts) {
 152:     Add-VMHost -Name $line.name  -Location (Get-Datacenter $datacenter -server $destVI) -user root -Password trunk@1 -Force
 153: }
 154:  
 155: #Turn on HA and DRS on 
 156: Set-Cluster -Server $destVI Cluster1 -DrsEnabled:$true -HAEnabled:$true -Confirm:$false
 157:  
 158: Disconnect-VIServer $sourceVI -Confirm:$false
 159:  
 160: ##workaround for non working new-vipermissions
 161:            
 162: function New-VIAccount($principal) {
 163:     $flags = [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::DeclaredOnly -bor [System.Reflection.BindingFlags]::Instance
 164:     
 165:     $method = $defaultviserver.GetType().GetMethods($flags) | where { $_.Name -eq "VMware.VimAutomation.Types.VIObjectCore.get_Client" }
 166:     $client = $method.Invoke($global:DefaultVIServer, $null)
 167:     Write-Output (New-Object VMware.VimAutomation.Client20.PermissionManagement.VCUserAccountImpl -ArgumentList $principal, "", $client)
 168: }
 169:  
 170: ##move the vm's to correct location
 171: $VMfolder = @()
 172: $VMfolder = import-csv "c:\VMs-with-FolderPath-$($datacenter).csv" | Sort-Object -Property Path
 173: foreach($guest in $VMfolder){
 174:     $key = @()
 175:     $key =  Split-Path $guest.Path | split-path -leaf
 176:     Move-VM (get-datacenter $datacenter -Server $destVI  | Get-VM $guest.Name) -Destination (get-datacenter $datacenter -Server $destVI | Get-folder $key) 
 177: }
 178:  
 179:  
 180: ##Import VM Custom Attributes and Notes
 181: $NewAttribs = Import-Csv "C:\vms-with-notes-and-attributes-$($datacenter).csv"
 182:  
 183:     foreach ($line in $NewAttribs) {
 184:         set-vm -vm $line.Name -Description $line.Notes -Confirm:$false
 185:         Set-CustomField -Entity (get-vm $line.Name) -Name $line.Key -Value $line.Value -confirm:$false
 186:         Set-CustomField -Entity (get-vm $line.Name) -Name $line.Key1 -Value $line.Value1 -confirm:$false
 187:     
 188:     }
 189:     
 190:  
 191: ##Import Permissions
 192: $permissions = @()
 193: $permissions = Import-Csv "c:\perms-$($datacenter).csv"
 194:  
 195: foreach ($perm in $permissions) {
 196:     $entity = ""
 197:     $entity = New-Object VMware.Vim.ManagedObjectReference
 198:     
 199:     switch -wildcard ($perm.EntityId)
 200:         {
 201:              Folder* { 
 202:              $entity.type = "Folder"
 203:              $entity.value = ((get-datacenter $datacenter | get-folder $perm.Foldername).ID).Trimstart("Folder-")
 204:              }
 205:              VirtualMachine* { 
 206:              $entity.Type = "VirtualMachine"
 207:              $entity.value = ((get-datacenter $datacenter | Get-vm $perm.Foldername).Id).Trimstart("VirtualMachine-")             
 208:             }
 209:         }
 210:     $setperm = New-Object VMware.Vim.Permission
 211:     $setperm.principal = $perm.Principal
 212:         if ($perm.isgroup -eq "True") {
 213:             $setperm.group = $true
 214:             } else {
 215:             $setperm.group = $false
 216:             }
 217:     $setperm.roleId = (Get-virole $perm.Role).id
 218:     if ($perm.propagate -eq "True") {
 219:             $setperm.propagate = $true
 220:             } else {
 221:             $setperm.propagate = $false
 222:             }
 223:                 
 224:     $doactual = Get-View -Id 'AuthorizationManager-AuthorizationManager'
 225:     $doactual.SetEntityPermissions($entity, $setperm)
 226: }
 227:     
 228: ##Error Checking
 229: ################
 230:  
 231: ##Gather all info for New Vcenter        
 232: ##Export all folders
 233: $report = @()
 234: $report = Get-folder vm -server $destVI | get-folder | Get-Folderpath
 235:         ##Replace the top level with vm
 236:         foreach ($line in $report) {
 237:         $line.Path = ($line.Path).Replace("DC1\","vm\")
 238:         }
 239: $report | Export-Csv "c:\Folders-with-FolderPath_dest.csv" -NoTypeInformation
 240:  
 241: ##Export all VM locations
 242: $report = @()
 243: $report = get-vm -server $destVI | Get-Folderpath
 244:  
 245: $report | Export-Csv "c:\vms-with-FolderPath_dest.csv" -NoTypeInformation
 246:  
 247:  
 248: #Get the Permissions    
 249: $permissions = Get-VIpermission -Server $destVI
 250:         
 251:         $report = @()
 252:               foreach($perm in $permissions){
 253:                 $row = "" | select EntityId, FolderName, Role, Principal, IsGroup, Propopgate
 254:                 $row.EntityId = $perm.EntityId
 255:                 $Foldername = (Get-View -id $perm.EntityId).Name
 256:                 $row.FolderName = $foldername
 257:                 $row.Principal = $perm.Principal
 258:                 $row.Role = $perm.Role
 259:                 $report += $row
 260:             }
 261:         $report | export-csv "c:\perms_dest.csv" -NoTypeInformation
 262:  
 263: ##Export VM Custom Attributes and notes
 264:  
 265: $vmlist = get-vm -Server $destVI
 266: $Report =@()
 267:     foreach ($vm in $vmlist) {
 268:         $row = "" | Select Name, Notes, Key, Value, Key1, Value1
 269:         $row.name = $vm.Name
 270:         $row.Notes = $vm | select -ExpandProperty Notes
 271:         $customattribs = $vm | select -ExpandProperty CustomFields
 272:         $row.Key = $customattribs[0].Key
 273:         $row.Value = $customattribs[0].value
 274:         $row.Key1 = $customattribs[1].Key
 275:         $row.Value1 = $customattribs[1].value    
 276:         $Report += $row
 277:     }
 278:  
 279: $report | Export-Csv "c:\vms-with-notes-and attributes_dest.csv" -NoTypeInformation
 280:  
 281: ##compare the source and destination - this part is not yet finished
 282: write-output "Folder-paths"
 283: Compare-Object -ReferenceObject (import-csv C:\vms-with-FolderPath.csv) (import-csv C:\vms-with-FolderPath_dest.csv) -IncludeEqual
 284:  
 285:  
 286: write-output "Notes & Attributes"
 287: Compare-Object -ReferenceObject (import-csv "C:\vms-with-notes-and attributes.csv") (import-csv "C:\vms-with-notes-and attributes_dest.csv") -IncludeEqual
 288:  
 289: write-output "Permissions"
 290: Compare-Object -ReferenceObject (import-csv C:\perms.csv | select * -ExcludeProperty EntityId) (import-csv C:\perms_dest.csv | select * -ExcludeProperty EntityId) -IncludeEqual
 291: Disconnect-VIServer -Server $destVI -Force -confirm:$false
 292:     

The script is commented pretty well throughout the script, I will update with more and a walkthrough of the script later.

Give me a shout if you have any questions with the script

The script can also be downloaded below

Save

22 comments:

Virtu-Al said...

Great script Maish, seriously good stuff

afokkema said...

Very cool script! excellent work Maish!

avlieshout said...

Maish,

You saved my day!. I was working on something similar. Need to build a new vCenter database as our current has some corruptions.

Although I'm missing some stuff.
-export/import clusters
-export/import resource pools
-export/import DRS affinity rules
-export/import roles
-export/import Scheduled tasks
-export/import vcenter server settings

-export/import Customization specifications. I posted a script for this a while ago. http://www.van-lieshout.com/2009/07/export-and-import-customization-profiles-using-powershell/

maybe to be included in a future release ;-)

Major credits increase, my friend :-)
Keep the good stuff coming.

afokkema said...

@avliesthout

Export DRS affinity rules: http://ict-freak.nl/2009/09/06/powercli-export-and-import-drs-rules-v2/

Maish said...

Alan, Avlieshout and Afokkema, thank you all for your enthusiasm.

True this is a version 1.0, this was done specifically for my environment and therefore not all things were included.

There were some quirks with the Templates as well, that will have to come in a future version.

I am sure that all of you will be able to build on this into a bigger and more comprehensive script.

Maish said...

Alan, Avlieshout and Afokkema, thank you all for your enthusiasm.

True this is a version 1.0, this was done specifically for my environment and therefore not all things were included.

There were some quirks with the Templates as well, that will have to come in a future version.

I am sure that all of you will be able to build on this into a bigger and more comprehensive script.

charlie said...

Hi there. Having some trouble with: $key = ($folder.Path -split "\\")[-2]

Error: you must provide a value expression on the right-hand side of the '-or' operator

Any chance you can help?

Tony Ganzer said...

Hello,
I tried to use the script customizing it slightly to fit my environment. The export process works well, but the import process completely failed. I haven't had time yet to work out what the issue was but I was wondering if you had problems getting the import process to work as well. Atleast with the export process I had and will continue to have a backup of that data. :) Thanks,

Tony

tylopes said...

Wow... you just saved me a bunch of time!!!! Thank you!

Terry said...

Hello Everyone,
I found my problem. I was using the same folder names in different parent folders. This was causing $entity.value = ((get-datacenter $datacenter | get-folder $perm.Foldername).ID).Trimstart("Folder-") to fail.

Thanks to anyone who looking into this matter.

Thanks again for the script.

Terry

Maish said...

Thanks for the update on you solution Terrry, I am glad you figured it out.

Maish said...

I am happy that you could make use of it tylopes.

Mike said...

I am experiencing the same issue. Did you manage to resolve?

Maish said...

Can you please explain what the problems you are experiencing?

Can you give a description of what your folder structure is like?

michael said...

Probably the reason is that "-split" operator is only available in PowerShell 2.0.
You should mention in the article that PowerShell 2.0 is required to run the script.

Great script

Maish said...

Thank you Michael for pointing this out - I will update the script.

virtual24 said...

first thanks for this script

which part sould i use when i ONLY want to export and import Custom Attributes ?

Natasha Vermilyea said...

Does this work for a move from 5.0 to 5.1?

Maish said...

Hi Natasha, I have not tried this in 5.x versions - so I cannot say.
If you do try, I would be interested in hearing how it turned out for you.

Robert said...

How does this script woth with vDS?

Ron said...

Hi Maish;

Is there a way to migrate VMs from one vCenter to another "without" removing and re-adding the ESXi hosts?? To be brief, I'm trying to migrate VMs and their corresponding folders/permissions from vCenter 4.1 to vCenter 5.1. Moving the hosts creates certain obstacles because of networking (vSS versus vDS) and ESXi licensing. Have you or anyone else had any success using this script in a vSphere/vCenter 5.1 environment?

I haven't been able to locate any PowerCli script yet that will help facilitate my requirement. Any help you can provide would be appreciated. Thanks.

Ron

Terry said...

Hello Maish,

I found your script and it looks like exactly what I need.

I am trying to use your script and have run into the following error during test of the "Import Permissons" section of the script.

Method invocation failed because [System.Object[]] doesn't contain a method named 'SetEntityPermissions'.
At H:\Scripts\PowerShell\snippet.ps1:54 char:35
+ $doactual.SetEntityPermissions <<<< ($entity, $setperm)
+ CategoryInfo : InvalidOperation: (SetEntityPermissions:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound

If I $doactual | gm I see:

SetEntityPermissions Method System.Void SetEntityPermissions(VMware.Vim.ManagedObjectReference entity, VMware.Vim.Permission[] permission)

I noticed the System.void. Does that mean it is no longer a valid method in Posh v2 / PowerCLI 4.0 U1 build 208462? If so do I need to run on Posh v1? or is there a workaround.

Thanks for the script and advanced thanks for any assistance.

Terry