Migrating virtual center to another virtual center

!!! WARNING !!!
this is not finished ! DO NOT RUN THIS ON PRODUCTION SYSTEMS !!! UNLESS YOU HAVE TESTED IT IN DEV !!!

Gents, please do not treat this as a ready-to-go solution, you would have to understand the contents, in order how to apply this. Destroying your production is that last thing i want.
I had to write this to recover a VC after a major bug/failure. I had to create another VirtualCenter in order to fix the issue. The main goal of this script is to move ‘everything’ i had in the old virtual center server to the new one.

PLEASE NOTE !!!
DVS is not supported here at this moment

Before you comment this, yes, i know that there are already some software/script for doing this, but in my opinion they were not doing everything or not in the way i wanted to do it. That’s why i created this.
This is not completed ! I have tested it numerous times on dev platforms, for me it was working. The only part which is not here is the cluster configs and dvs. Script will not configure your virtual center, but just the ‘content’ including, folder (of any kind), dc, clusters, vapps. The placements + permissions. The annotations are written in previous posts as well as roles.
I know it’s messy, yes… Although i think i will not spend more time on this, unless i will have to do so. I just hope that it might help for people that had similar task to do, and they were lacking some features in their clone of virtual center.
I DO NOT GUARANTEE THE RESULTS OF THE SCRIPT. Gents, please test this first in your test/dev environments. I have been running this script at least 600-800 times, until i was happy with the outcome, where it worked for me. Please have in mind it will not take into consideration on DVS setup. I’ve come up with 2 solutions for dvs, but never implemented it in it. I have decided to write this on my own mainly to test myself if i am able to do so. There might be better ways of doing it(100% sure of that 😉 ). I have put some comments in the script so you can know what to expect at which moment, the execution of this script is divided in many parts. It’s because i never got to 100% with this script i left myself spots to understand where it can go wrong.
I am sorry in some way that i did not explain this entirely, but i would have spend serious amount of hours on that…
I will have to check if i have posted the role-cloning script on blog, because this is something that is expected to be in place for this to work.

#READ THIS VARIABLE FIRST!!!
#$ProjectNAme

function traverse-HostView3 
{
param(
[Parameter(Mandatory=$true)]$originVC,
[Parameter(Mandatory=$true)]$root,
[Parameter(Mandatory=$true)]$type,
[Parameter(Mandatory=$true)]$destinationroot,
[Parameter(Mandatory=$true)]$destinationVC,
[Parameter(Mandatory=$true)]$DatabaseFile,
[string]$DestMorefDC
)

$permissionFile = $DatabaseFile+'-Permissions.csv'

#the type can be following :
#HostFolder - For the Host/Clusters view
#

#Datacenter Object does not contain ChildEntity property, it is a point where folders are split on
#VmFolder            
#HostFolder          
#DatastoreFolder     
#NetworkFolder       
#Hence when we encounter the datacenter object, we are switching root obj to become a HostFolder of that root/Datacenter

#"function start:: , passed root is now "+$root.name

"my root moref type is now "+$root.moref
"Child entities inside this root are: "+($root.ChildEntity -join ',')
#"Will start now the IF section"

################
################ WHAT TO DO IN ORDER TO GET THE TOP ROOT FOLDER -D1 , to get his permissions, since iteration starts from children ?!?!
################ Make entry at the verybottom for folder -d1 , and just take all settings from it, as 1 -off
if ($root.moref.type -eq 'Datacenter') 
{ 
$root = get-view -id $root.$type -server $originVC
}
if ($root.moref.type -eq 'ClusterComputeResource')
{
#break
#we are 'cheating' here to get the compliance for the ChildEntity. Since Cluster does not have ChildEntity that would describe all its hosts. We are creating a column with ChildEntity that include all hosts
#This will also make a situation that resourcepools will not be checked, since we redirected already path to Hosts, we can also add to Children resource pools later on.
$root =$root | Select *,@{n='ChildEntity';e={$_.Host}}
}

if ($root.moref.type -eq 'VirtualApp')
{
#we are 'cheating' here to get the compliance for the ChildEntity. Since Cluster does not have ChildEntity that would describe all its hosts. We are creating a column with ChildEntity that include all hosts
#This will also make a situation that resourcepools will not be checked, since we redirected already path to Hosts, we can also add to Children resource pools later on.
$root =$root | Select *,@{n='ChildEntity';e={$_.Vm}}
}

#if ($root.moref.type -eq 'Datastore') {break}

#"If section was finished, now we will be collecting children"

if ($root.ChildEntity)
{
    $children = get-view -id $root.ChildEntity -server $originVC
    foreach ($child in $children)
    {
       "!!!!!"+ $child.name + " :: "+ $child.moref
       # Switch [typ]
       switch ($child.moref.type) 
    { 
        'Datacenter' {
        "We hit Datacenter"
        $CreatedObject = Get-view  (get-view -id ($destinationroot.createdatacenter($child.name)) -server $destinationVC).HostFolder -server $destinationVC
        $CreatedDatacenter = get-view -id $CreatedObject.parent -Server $destinationVC 
        #sourcedatacenter = $child
        #Datacenter reference database
        "showing object"
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$child.moref.ToString()}},@{n='DestinationObjectMoref';e={$CreatedDatacenter.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        #$SourceDCPermission = g
        
        #$DCobject = get-view -id $child.parent -Server $originvc
        
        if ($child.Permission){ 
        write-host  "I saw permissions inside the DC object" 
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$CreatedDatacenter.Moref.ToString()}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }

        } 
        'Folder' {
        "We hit folder:"
        $CreatedObject = get-view $destinationroot.createfolder($child.name) -server $destinationVC
        #$CreatedObject = get-view (New-Folder -Server $destinationvc -name $child.name -Location ($destinationroot|Get-VIObjectByVIView)).Extensiondata
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$child.moref.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile        
        #sourceFolderPermissions
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        } 
        'ClusterComputeResource' {
        "We hit cluster"
        $CreatedObject = get-view $destinationroot.createcluster($child.name,(new-object VMware.Vim.ClusterConfigSpec)) -server $destinationVC
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$child.moref.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        #Permissions
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }

        } 
        'VirtualApp' {"We hit VirtualApp"
        #This can only be done, when hosts are attached to cluster.
        #Createvapp is method on default resource pool inside cluster.
        #VMware.Vim.ManagedObjectReference CreateVApp(string name, VMware.Vim.ResourceConfigSpec resSpec, VMware.Vim.VAppConfigSpec configSpec,VMware.Vim.ManagedObjectReference vmFolder)
        $child.vappconfig.EntityConfig | export-clixml -Encoding 'UTF8' -depth 2 -LiteralPath ('c:\'+$child.vappconfig.InstanceUuid+'EntityConfig')
        $child.Config.CpuAllocation | export-clixml -Encoding 'UTF8' -depth 2 -LiteralPath ('c:\'+$child.vappconfig.InstanceUuid+'CpuAllocation')
        $child.Config.MemoryAllocation | export-clixml -Encoding 'UTF8' -depth 2 -LiteralPath ('c:\'+$child.vappconfig.InstanceUuid+'MemoryAllocation')
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$child.moref.ToString()}},@{n='DestinationObjectMoref';e={$destinationroot.Moref.ToString()}},@{n='Objuuid';e={$child.VAppConfig.InstanceUuid}},@{n='DestMorefDC';e={}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={$child.owner.tostring()}}, @{n='vmmoref';e={}}  | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$child.name}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        }
        'HostSystem' {
        "We hit HostSystem"
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$root.moref.ToString()}},@{n='DestinationObjectMoref';e={$destinationroot.Moref.ToString()}},@{n='Objuuid';e={$child.Hardware.systeminfo.uuid}},@{n='DestMorefDC';e={$DestMorefDC}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$child.Hardware.systeminfo.uuid}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        } 
        'VirtualMachine' {
        #FOR VMS YOU HAVE TO SELECT .config.uuid property into the csv, in order to hit the proper vm. ## TODO !!!!!
        "We hit VirtualMachine"
        $vmtxpath=if ($child.config.template){$child.config.files.VmPathName}
        $vmtxHostRegistered = if ($vmtxpath) { (get-view $child.Runtime.Host -property name -Server $originvc).Name }
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$root.moref.ToString()}},@{n='DestinationObjectMoref';e={$destinationroot.Moref.ToString()}},@{n='Objuuid';e={$child.config.uuid}},@{n='DestMorefDC';e={$DestMorefDC}}, @{n='vmtxpath';e={$vmtxpath}}, @{n='vmtxHost';e={$vmtxHostRegistered}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={$child.moref.toString()}} | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile    
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$child.config.uuid}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        } 
        'Datastore' {"
        We hit Datastore"
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$root.moref.ToString()}},@{n='DestinationObjectMoref';e={$destinationroot.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={$DestMorefDC}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$child.name}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        }
        'StoragePod' {"
        We hit StoragePod"
        $CreatedObject = get-view $destinationroot.createstoragepod($child.name) -server $destinationVC
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$child.moref.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        }
        #The ComputeResource if for a single Host inside a folder for example
        #'ComputeResource' {"
        #We hit StoragePod"
        #$CreatedObject = get-view $destinationroot.createstoragepod($child.name) -server $destinationVC
        #'' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$child.moref.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={}} | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        #if ($child.permission){  
        #$child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$CreatedObject.Moref.ToString()}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        #}
        #}
        'Network' {
        "We hit network"
        '' | Select-object @{n='ObjectName';e={$child.name}},@{n='OriginObjectMoref';e={$root.moref.ToString()}},@{n='DestinationObjectMoref';e={$destinationroot.Moref.ToString()}},@{n='Objuuid';e={}},@{n='DestMorefDC';e={$DestMorefDC}},@{n='vmtxpath';e={}}, @{n='vmtxHost';e={}}, @{n='OwnerMoref';e={}}, @{n='vmmoref';e={}}   | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $DatabaseFile
        if ($child.permission){  
        $child.Permission | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$child.name}},@{n='DestMorefDC';e={$DestMorefDC}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $permissionFile
        }
        }  
        default {"The object could not be determined : please check";read-host}
    }
       traverse-HostView3 -root $child -type $type -originVC $originVC -destinationVC $destinationVC -destinationroot $CreatedObject -DatabaseFile 'c:\databasefile.csv' -DestMorefDC $DestMorefDC
       
    }
}

}



$originvc = 'your.original.vc'
$destinationVC = 'your.new.vc'
connect-viserver $originVC -Credential (Get-credential)
connect-viserver $destinationVC -credential (get-credential)
$pscred = Get-Credential

$rootFolder = get-view -id ($global:DefaultVIServers |? {$_.name -eq $originvc}).extensiondata.content.rootfolder -Server $originvc
$destinationroot = get-view -id ($global:DefaultVIServers |? {$_.name -eq $destinationvc}).extensiondata.content.rootfolder -Server $destinationvc

traverse-HostView3 -root $rootFolder -type 'HostFolder' -originVC $originVC -destinationVC $destinationVC -destinationroot $destinationroot -DatabaseFile 'c:\databasefile.csv'



##You start first by running through Host and Clusters View, since you have to recreate the entire main structure for datacenters.
##Remember that after each run , in case you went into trouble to clear the database file, or you different file.
##
##
##Tutaj podajemy root folder w source VC, jako folder do konkretnego DC.networkFolder , tak samo w destination , nakladamy folder dc.networkfolder
#NETWORK FOLDERS:
foreach($dc in (import-csv 'c:\databasefile.csv' -Encoding 'UTF8' |? {$_.OriginObjectMoref -like "Datacenter-*"})  )
{
traverse-HostView3 -root (get-view (get-view $dc.OriginObjectMoref -server $originVC).NetworkFolder -Server $originVC) -type 'NetworkFolder' -originVC $originVC -destinationVC $destinationVC -destinationroot (get-view (get-view $dc.DestinationObjectMoref -server $destinationVC).NetworkFolder -Server $destinationVC) -DatabaseFile 'c:\databasefile.csv' -DestMorefDC ($dc.DestinationObjectMoref) 
}

#VM FOLDERS:


foreach($dc in (import-csv 'c:\databasefile.csv' -Encoding 'UTF8' |? {$_.OriginObjectMoref -like "Datacenter-*"})  )
{
traverse-HostView3 -root (get-view (get-view $dc.OriginObjectMoref -server $originVC).VmFolder -Server $originVC) -type 'VmFolder' -originVC $originVC -destinationVC $destinationVC -destinationroot (get-view (get-view $dc.DestinationObjectMoref -server $destinationVC).VmFolder -Server $destinationVC) -DatabaseFile 'c:\databasefile.csv' -DestMorefDC ($dc.DestinationObjectMoref) 
}

#Storage FOLDERS:
foreach($dc in (import-csv 'c:\databasefile.csv' -Encoding 'UTF8' |? {$_.OriginObjectMoref -like "Datacenter-*"})  )
{
traverse-HostView3 -root (get-view (get-view $dc.OriginObjectMoref -server $originVC).DatastoreFolder -Server $originVC) -type 'DatastoreFolder' -originVC $originVC -destinationVC $destinationVC -destinationroot (get-view (get-view $dc.DestinationObjectMoref -server $destinationVC).DatastoreFolder -Server $destinationVC) -DatabaseFile 'c:\databasefile.csv' -DestMorefDC ($dc.DestinationObjectMoref) 
}

###
###  COPY ROOT VC PERMISSION HERE
### 
### THIS PART IS ONLY IF YOUR LOCAL SSO DOMAIN CHANGES, MODIFY THIS IF IT IS THE SAME
$sourceSSODomain = 'VSPHERE.LOCAL'
$destinationSSODomain = 'TEST.LOCAL'

$tpermissionFile = 'databasefile.csv-Permissions.csv'
$VCSourceRootPermissions = (get-view -id 'Folder-group-d1' -server $originvc).permission |?{$_.Principal -notmatch "vpxd-|vsphere-webclient-"} 
#IF you don't want to change the local sso domain , hash the line below:
$VCSourceRootPermissions|? {$_.principal -match $sourceSSODomain} | %{$_.principal = [regex]::replace($_.Principal,$sourceSSODomain,$destinationSSODomain)}

$VCSourceRootPermissions | select @{n='OriginObjectMoref';e={$_.Entity.ToString()}},@{n='DestinationObjectMoref';e={$_.Entity.ToString()}}, Principal,RoleId,Propagate,Group | Export-Csv -NoTypeInformation -Append -Encoding 'UTF8' -LiteralPath $tpermissionFile


##
## END OF THE COPY OF VC PERMISSION
##



##
##Before replaying permissions you have to be sure that the SSO domain is the same for permissions, therefore we will replace old SSO domain with new SSO domain.
##
$RoleDB = Import-Csv -Encoding UTF8 -LiteralPath 'C:\RoleDB.csv'
$sourceSSODomain = (Get-AdvancedSetting -Entity ($global:DefaultVIServers|?{$_.Name -eq $originvc}) -Name 'config.vpxd.sso.default.admin').value.split('@')[1].ToUpper()
$destinationSSODomain = (Get-AdvancedSetting -Entity ($global:DefaultVIServers|?{$_.Name -eq $destinationvc}) -Name 'config.vpxd.sso.default.admin').value.split('@')[1].ToUpper()
$tpermissionFile = 'c:\databasefile.csv-Permissions.csv'
$permissions = import-csv -Encoding UTF8 -LiteralPath $tpermissionFile 
$permissions |% { $_.principal = [regex]::replace($_.Principal,$sourceSSODomain,$destinationSSODomain)  }
$permissions | export-csv -Encoding UTF8 -LiteralPath $tpermissionFile -NoTypeInformation

##
## REPLAY PERMISSIONS
##
$RoleDB = Import-Csv -Encoding UTF8 -LiteralPath 'C:\RoleDB.csv'
#$rolehash = @{}
#$roleDB | % { $roleHash += @{$_.SourceRoleId = get-virole -id $_.DestinationRoleID -Server $destinationVC}  }
#RoleHash is now [ID_SOURCE_ROLE]  || [ROLE_OBJECT_ON_DESTINATION]
#OriginObjectMoref      : Folder-group-v450
#DestinationObjectMoref : Folder-group-v972
#DestMorefDC            : Datacenter-datacenter-953
#Principal              : VSPHERE.LOCAL\greg
#RoleId                 : -1
#Propagate              : True
## FOLDERS DCs
## FOLDERS Hs
#### We Will need authorizationmanager from the destination VC now. method SetEntityPermissions         Method     void SetEntityPermissions(VMware.Vim.ManagedObjectReference entity, VMware.Vim.Permission[] permission)
##Entity     :
#Principal  :
#Group      : False
#RoleId     : 0
#Propagate  : False
##
##


$DestAM = get-view -id ($global:DefaultVIServers|?{$_.Name -eq $destinationvc}).extensiondata.content.AuthorizationManager -Server $DestinationVC

##
## HOSTS VIEW FOLDERS
##
Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "Folder-group-h*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}

##
## DATACENTERS NOW
##

Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "Datacenter-datacenter-*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)  
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}

##
## CLUSTERS NOW
##

Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "ClusterComputeResource-domain-c*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)  
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}

##
## NETWORK VIEW FOLDERS
##
Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "Folder-group-n*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}

##
## VM VIEW FOLDERS
##
Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "Folder-group-v*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}

##
## STORAGE FOLDERS VIEW FOLDERS
##
Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "Folder-group-s*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}

##
## STORAGE PODS VIEW FOLDERS
##
Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "StoragePod-group-p*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
$DestAM.SetEntityPermissions($entity.DestinationObjectMoref, $DestinationObjectPermission)
}


#Adding Hosts to cluster
Foreach ($cluster in ((import-csv -LiteralPath 'c:\databasefile.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "ClusterComputeResource-domain-c*"}) |?{$_.Objuuid} | Group-Object -Property destinationObjectMoref).group) 
{ 
$VMhostsInCluster = $cluster
$Clusterdestination = $VMhostsInCluster[0].DestinationObjectMoref
$location = get-view -id  $Clusterdestination -server $destinationVC| Get-VIObjectByVIView
foreach ($vmhost in $VMhostsInCluster) 
{
 Add-VMHost -Name $vmhost.ObjectName -Location $location -Credential $pscred -Force -Server $destinationVC
 }

}
#make sure that all pwd are the same

##
##Once all hosts are in place in the VC , we attach our templates
##
foreach ($vmtemplate in  (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.OriginObjectMoref -like "Folder-group-v*" -and $_.vmtxpath})){
$location = get-view -id $vmtemplate.DestinationObjectMoref -server $destinationVC | Get-VIObjectByVIView
New-Template -Name $vmtemplate.ObjectName -TemplateFilePath $vmtemplate.vmtxpath -Location $location -vmhost $vmtemplate.vmtxHost
}


##
## VMHOSTS PERMISSIONS
##
Foreach ($entity in (import-csv -LiteralPath 'c:\databasefile.csv-permissions.csv' -Encoding UTF8 | ?{$_.OriginObjectMoref -like "HostSystem-host-*"})){
$DestinationObjectPermission = New-Object VMware.vim.Permission
$DestinationObjectPermission.Principal = $entity.Principal
$DestinationObjectPermission.Group = [System.Convert]::ToBoolean($entity.Group)
$DestinationObjectPermission.RoleId = ($roleDB|?{$_.SourceRoleId -eq ($entity.RoleId)}).DestinationRoleId
$DestinationObjectPermission.Propagate = [System.Convert]::ToBoolean($entity.Propagate)
$DestinationObjectPermission
#Here our destinationObjectMoref is actually vm host uuid value , not moref. We couldn't have moref as it was unknown.
$DestinationVMHost = get-view -ViewType HostSystem -Filter @{'Hardware.SystemInfo.Uuid'=$entity.DestinationObjectMoref} -Property Name -Server $destinationVC
#$destinationVMHost
$DestAM.SetEntityPermissions($DestinationVMHost.moref, $DestinationObjectPermission)
}



##
## Moving object to their correct location
##
##
## NETWORK VIEW ENTITIES
##!!!!!!!!!!!!!!!!!!!!!!!!!! We should move only those which are not located at the datacenter, otherwise , waste of time.
$DCnetworkFolderIDs = @((get-view -id ((import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.OriginObjectMoref -like "Folder-group-n*" -and $_.DestMorefDC -like "Datacenter-datacenter-*"}) |group-object -Property DestMorefDC).Name -Server $destinationVC).NetworkFolder |% {$_.ToString()})
$allNetworksInFolders = (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.OriginObjectMoref -like "Folder-group-n*" -and $_.DestMorefDC -like "Datacenter-datacenter-*"}) | ? {$DCnetworkFolderIDs -notcontains $_.DestinationObjectMoref}
Foreach ($entity in $allNetworksInFolders){
#DestinationObjectMoref : Folder-group-n1575
$dof = get-view -id $entity.DestinationObjectMoref -server $destinationVC
$ntwkmoref = (get-view -server $destinationVC -searchroot $entity.DestMorefDC -viewtype 'Network' -filter @{'name'=$entity.ObjectName}).moref
$dof.MoveIntoFolder($ntwkmoref)
}

## VM VIEW ENTITIES
##!!!!!!!!!!!!!!!!!!!!!!!!!! We should move only those which are not located at the datacenter, otherwise , waste of time.
Foreach ($entity in (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.OriginObjectMoref -like "Folder-group-v*" -and $_.DestMorefDC -like "Datacenter-datacenter-*"})){
#DestinationObjectMoref : Folder-group-n1575
'Object'
$entity

$dof = get-view -id $entity.DestinationObjectMoref -server $destinationVC
'Destination'
$dof.name
$vmmoref = (get-view -server $destinationVC -searchroot $entity.DestMorefDC -viewtype 'Virtualmachine' -filter @{'Config.Uuid'=$entity.Objuuid}).moref
'VM Object'
$vmmoref
$dof.MoveIntoFolder($vmmoref)
}

##
## Creating Vapps, moving VMs into Vapps, Applying Vapp start order
##
Foreach ($entity in (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.OriginObjectMoref -like "VirtualApp-resgroup-v*" -and $_.OwnerMoref})){
#entity is now
#ObjectName             : LABS_Greg
#OriginObjectMoref      : VirtualApp-resgroup-v241
#DestinationObjectMoref : Folder-group-v4284
#Objuuid                : 5038914a-df12-abcc-77aa-2d2991ccaa2e
#DestMorefDC            :
#vmtxpath               :
#vmtxHost               :
#OwnerMoref             : ClusterComputeResource-domain-c7

#First we prepare the configs for our Vapp entity
$EntityMemoryAllocationXML = Import-Clixml -LiteralPath ('C:\'+$entity.Objuuid+'MemoryAllocation')
$EntityCpuAllocationXML = Import-Clixml -LiteralPath ('C:\'+$entity.Objuuid+'CpuAllocation')
$EntityConfig = Import-Clixml -LiteralPath ('C:\'+$entity.Objuuid+'EntityConfig')
$resSpec = New-Object VMware.Vim.ResourceConfigSpec
$resSpec.cpuAllocation = New-Object VMware.Vim.ResourceAllocationInfo
$resSpec.cpuAllocation.reservation = $EntityCpuAllocationXML.Reservation
$resSpec.cpuAllocation.expandableReservation = $EntityCpuAllocationXML.expandableReservation
$resSpec.cpuAllocation.limit = $EntityCpuAllocationXML.limit
$resSpec.cpuAllocation.shares = New-Object VMware.Vim.SharesInfo
$resSpec.cpuAllocation.shares.shares = $EntityCpuAllocationXML.shares.shares
$resSpec.cpuAllocation.shares.level = $EntityCpuAllocationXML.shares.level
$resSpec.memoryAllocation = New-Object VMware.Vim.ResourceAllocationInfo
$resSpec.memoryAllocation.reservation = $EntityMemoryAllocationXML.Reservation
$resSpec.memoryAllocation.expandableReservation = $EntityMemoryAllocationXML.expandableReservation
$resSpec.memoryAllocation.limit = $EntityMemoryAllocationXML.limit
$resSpec.memoryAllocation.shares = New-Object VMware.Vim.SharesInfo
$resSpec.memoryAllocation.shares.shares = $EntityMemoryAllocationXML.shares.shares
$resSpec.memoryAllocation.shares.level = $EntityMemoryAllocationXML.shares.level

$configSpec = New-Object VMware.Vim.VAppConfigSpec
$configSpec.InstanceUuid = $entity.Objuuid
$vmFolder = $Entity.DestinationObjectMoref


#(import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8| ?{$_.OriginObjectMoref -eq $data.OwnerMoref -and !$_.Objuuid})
#$ClusterResourcePool = (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8| ?{$_.OriginObjectMoref -eq $data.Objuuid -and !$_.Objuuid}).DestinationObjectMoref

$ClusterResourcePool = get-view -id (get-view -id (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8| ?{$_.OriginObjectMoref -eq $entity.OwnerMoref -and !$_.Objuuid}).DestinationObjectMoref -server $destinationVC).ResourcePool -Server $destinationVC

$CreatedVapp = get-view -id  $ClusterResourcePool.CreateVApp($entity.ObjectName, $resSpec, $configSpec, $vmFolder) -Server $destinationVC

if ( $CreatedVapp.vappconfig.InstanceUuid -ne $entity.Objuuid ) { Write-Error "We have failed to created vapp with the same uuid"}

#Moving vms into the vAPP
$VMsOldMorefs = $EntityConfig.Key|%{$_.toString()}
$OldVMs = import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? { $VMsOldMorefs -contains $_.vmmoref }
$newVMs = $oldVMs.Objuuid | % {get-view -viewtype virtualmachine -Filter @{'config.uuid'=$_} -Server $destinationVC}
$createdvapp.MoveIntoResourcePool($newVMs.moref)
#Let's make proper order in vapp

foreach ($entry in $EntityConfig)
{
$oldkey = (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.vmmoref -eq $entry.Key.ToString() }).Objuuid
$newkey = (get-view -viewtype virtualmachine -Filter @{'config.uuid'=$oldkey} -Server $destinationVC).moref
$entry.key=$newkey

}

$newconfig = new-object VMware.Vim.VAppConfigSpec
foreach ($entry in $EntityConfig)
{

$newEntityconfig =  new-object VMware.Vim.VAppEntityConfigInfo
$newEntityconfig.Key = $entry.key
$newEntityconfig.Tag = $entry.tag
$newEntityconfig.StartOrder = $entry.StartOrder
$newEntityconfig.StartDelay = $entry.StartDelay
$newEntityconfig.WaitingForGuest = $entry.WaitingForGuest
$newEntityconfig.StartAction = $entry.StartAction
$newEntityconfig.StopDelay = $entry.StopDelay
$newEntityconfig.StopAction = $entry.StopAction
$newEntityconfig.DestroyWithParent = $entry.DestroyWithParent

$newConfig.EntityConfig += $newEntityconfig

}

$CreatedVapp.UpdateVAppConfig($newConfig)
}
#we have now updated entityconfig

For the vapps part :

Foreach ($entity in (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.OriginObjectMoref -like "VirtualApp-resgroup-v*" -and $_.OwnerMoref})){
#entity is now
#ObjectName             : LABS_Greg
#OriginObjectMoref      : VirtualApp-resgroup-v241
#DestinationObjectMoref : Folder-group-v4284
#Objuuid                : 5038914a-df12-abcc-77aa-2d2991ccaa2e
#DestMorefDC            :
#vmtxpath               :
#vmtxHost               :
#OwnerMoref             : ClusterComputeResource-domain-c7

#First we prepare the configs for our Vapp entity
$EntityMemoryAllocationXML = Import-Clixml -LiteralPath ('C:\'+$data.Objuuid+'MemoryAllocation')
$EntityCpuAllocationXML = Import-Clixml -LiteralPath ('C:\'+$data.Objuuid+'CpuAllocation')
$EntityConfig = Import-Clixml -LiteralPath ('C:\'+$data.Objuuid+'EntityConfig')
$resSpec = New-Object VMware.Vim.ResourceConfigSpec
$resSpec.cpuAllocation = New-Object VMware.Vim.ResourceAllocationInfo
$resSpec.cpuAllocation.reservation = $EntityCpuAllocationXML.Reservation
$resSpec.cpuAllocation.expandableReservation = $EntityCpuAllocationXML.expandableReservation
$resSpec.cpuAllocation.limit = $EntityCpuAllocationXML.limit
$resSpec.cpuAllocation.shares = New-Object VMware.Vim.SharesInfo
$resSpec.cpuAllocation.shares.shares = $EntityCpuAllocationXML.shares.shares
$resSpec.cpuAllocation.shares.level = $EntityCpuAllocationXML.shares.level
$resSpec.memoryAllocation = New-Object VMware.Vim.ResourceAllocationInfo
$resSpec.memoryAllocation.reservation = $EntityMemoryAllocationXML.Reservation
$resSpec.memoryAllocation.expandableReservation = $EntityMemoryAllocationXML.expandableReservation
$resSpec.memoryAllocation.limit = $EntityMemoryAllocationXML.limit
$resSpec.memoryAllocation.shares = New-Object VMware.Vim.SharesInfo
$resSpec.memoryAllocation.shares.shares = $EntityMemoryAllocationXML.shares.shares
$resSpec.memoryAllocation.shares.level = $EntityMemoryAllocationXML.shares.level

$configSpec = New-Object VMware.Vim.VAppConfigSpec
$configSpec.InstanceUuid = $entity.Objuuid
$vmFolder = $Entity.DestinationObjectMoref


#(import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8| ?{$_.OriginObjectMoref -eq $data.OwnerMoref -and !$_.Objuuid})
#$ClusterResourcePool = (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8| ?{$_.OriginObjectMoref -eq $data.Objuuid -and !$_.Objuuid}).DestinationObjectMoref

$ClusterResourcePool = get-view -id (get-view -id (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8| ?{$_.OriginObjectMoref -eq $data.OwnerMoref -and !$_.Objuuid}).DestinationObjectMoref -server $destinationVC).ResourcePool -Server $destinationVC

$CreatedVapp = get-view -id  $ClusterResourcePool.CreateVApp($data.ObjectName, $resSpec, $configSpec, $vmFolder) -Server $destinationVC

if ( $CreatedVapp.vappconfig.InstanceUuid -ne $entity.Objuuid ) { Write-Error "We have failed to created vapp with the same uuid"}

#Moving vms into the vAPP
$VMsOldMorefs = $EntityConfig.Key|%{$_.toString()}
$OldVMs = import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? { $VMsOldMorefs -contains $_.vmmoref }
$newVMs = $oldVMs.Objuuid | % {get-view -viewtype virtualmachine -Filter @{'config.uuid'=$_} -Server $destinationVC}
$createdvapp.MoveIntoResourcePool($newVMs.moref)
#Let's make proper order in vapp

foreach ($entry in $EntityConfig)
{
$oldkey = (import-csv -LiteralPath C:\databasefile.csv -Encoding UTF8 | ? {$_.vmmoref -eq $entry.Key.ToString() }).Objuuid
$newkey = (get-view -viewtype virtualmachine -Filter @{'config.uuid'=$oldkey} -Server $destinationVC).moref
$entry.key=$newkey

}

$newconfig = new-object VMware.Vim.VAppConfigSpec
foreach ($entry in $EntityConfig)
{

$newEntityconfig =  new-object VMware.Vim.VAppEntityConfigInfo
$newEntityconfig.Key = $entry.key
$newEntityconfig.Tag = $entry.tag
$newEntityconfig.StartOrder = $entry.StartOrder
$newEntityconfig.StartDelay = $entry.StartDelay
$newEntityconfig.WaitingForGuest = $entry.WaitingForGuest
$newEntityconfig.StartAction = $entry.StartAction
$newEntityconfig.StopDelay = $entry.StopDelay
$newEntityconfig.StopAction = $entry.StopAction
$newEntityconfig.DestroyWithParent = $entry.DestroyWithParent

$newConfig.EntityConfig += $newEntityconfig

}

$CreatedVapp.UpdateVAppConfig($newConfig)
}
#we have now updated entityconfig

Advertisements

readd-permission

This is only usefull if you get into problems with VC, if everything is ok then powercli cmdlet is more than enough for managing permissions. I could not do it via powercli cmdlet because i was receving error (user already exists, which is sort of true, but that due to the ‘issue’ i had).
Kudos to William Lam LINK He was describing accessing other methods, really helpful article for what i had to do.
Via powershell you will be calling methods in the same way as you would do it via mob(via your web browser)
The way i did this particular one is that , first you would have to login to VC, then get permissions in some variable so , $perms=get-vipermission
Then you would call it readd-permission -permission $perms[4]
for example. if would transform the [domain]\username to [full.domain.name]\username and later on will add the permission on exactly the same object, in the same way as the permission you are sending was built. I also left 1 parameter if you want to edit on the fly the username , so with: -user ‘shortdomain\username’ you will be replacing the current user on the permission you sent to the function.
I don’t want to go too deep with the issue i had but, all in all i had issues with not being able to search entities (any kind, vm, network, datastore,host).

function readd-permission{
[CmdletBinding()]
param(
[parameter(Mandatory=$true)]
[VMware.VimAutomation.Types.PermissionManagement.Permission]
$permission,
$user,
$vcenter=$global:DefaultVIServer.name
)
$entity=$permission.EntityId
$principal=$permission.Principal
$isgroup=$permission.isgroup
if($user){$principal=$user}
$udomain=$principal.Split('\')[0]
$domain = (((([ADSI]"LDAP://$udomain").DistinguishedName).Split('DC=')).replace(',','') |?{$_}) -join '.'
$properusername=$principal.Split('\')[1]+'@'+$domain
$principal=[uri]::EscapeDataString($properusername)
[uri]::EscapeUriString($properusername)
$propagate=$permission.Propagate
$roleid=$permission.ExtensionData.RoleId
[regex]$pattern = '-'
$goodentity = [uri]::EscapeDataString($pattern.replace($entity,':',1))
$mob_url = "https://$vcenter/invsvc/mob3/?moid=authorizationService&method=AuthorizationService.AddAccessControlList"
$sessionnonce = (Invoke-WebRequest -Uri $mob_url -SessionVariable vmware -Credential $cred -Method GET).inputfields[0].value
$iuuid = $global:DefaultVIServer.InstanceUuid
$body = @"
vmware-session-nonce=$sessionnonce&docUri=urn%3Avmomi%3A$goodentity%3A$iuuid&permissions=%3Cpermissions%3E%0D%0A+++%3Cprincipal%3E%0D%0A++++++%3Cname%3E$principal%3C%2Fname%3E%0D%0A++++++%3Cgroup%3E$isgroup%3C%2Fgroup%3E%0D%0A+++%3C%2Fprincipal%3E%0D%0A+++%3Croles%3E$roleid%3C%2Froles%3E%0D%0A+++%3Cpropagate%3E$propagate%3C%2Fpropagate%3E%0D%0A+++%3Cversion%3E42%3C%2Fversion%3E%0D%0A%3C%2Fpermissions%3E
"@ 
Invoke-WebRequest -Uri $mob_url -WebSession $vmware -Method POST -Body $body
$body
$result = (get-view -id $entity).permission
return $result
}

before running this , you would already need the $cred variable prepared + ignore ssl issue

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    
    public class IDontCarePolicy : ICertificatePolicy {
        public IDontCarePolicy() {}
        public bool CheckValidationResult(
            ServicePoint sPoint, X509Certificate cert,
            WebRequest wRequest, int certProb) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy 
$cred = get-credential

Get-gEvents for getting vsphere events with some additional filtering

Some time ago i wrote a dirty function that utilizes eventfilterspec to filter out events. I knew it had one flaw, because as far as i remember i was using there QueryEvents method. Nothing bad with that, but as far as i understand it will only return up to 1000 records. So i have finally modified it so it uses EventCollector. It has support for searching by eventchainID, eventTypeId, username, date. I have tried to document function as much as possible. For me it works ok. There is some tiny support for checking eventTypeIds inside the function. If something is not clear make sure you will read the help
get-help get-gEvents -full
One confusing bit might be that while passing the entityname , you have to pass the viewtype as well. Well i did it because i use it this way 😉 So i use Get-View -ViewType [type] -Filter @{‘name’=XXX’}
So it’s up to you what exactly you are looking for, while specifying the entity, In filter name will be put, but ViewType specifies the type of object, whether it will be a datacenter, vm , cluster, datastore etc..
I tried to implement the switch for enabling/disabling full formatted message, but for some reason it was not working, no idea why.

Quick examples:

How to check how many vmotions DRS has performed inside a particular cluster ?

(Get-gEvents -StartDate (get-date).AddDays(-1) -types 'VmBeingHotMigratedEvent' -EntityName 'Cluster_name' -ViewType ClusterComputeResource).Count

How to check how many vmotions DRS has performed inside a particular datacenter ?

(Get-gEvents -StartDate (get-date).AddDays(-1) -types 'VmBeingHotMigratedEvent' -EntityName 'Datacenter_name' -ViewType Datacenter).count

Endless possibilities basically.. i will throw 1 more
How many times user X has powered off a VM in last 7 days

(Get-gEvents -StartDate (get-date).AddDays(-7) -EventTypeIds 'VmPoweredOffEvent' -EventUsername 'DOMAIN\UserX' -systemUser:$false ).count

How many were there vmotions in some cluster during last 7 days

Get-gEvents -StartDate (get-date).AddDays(-7) -EventTypeIds 'VmFailedMigrateEvent' -EntityName 'SomeCluster' -ViewType ClusterComputeResource}

What if you want to check for other dates ? Use: -StartDate with (get-date).AddDays(-7) for example, to go 7 days back. Same goes for end date, -EndDate (get-date…..

When you will be giving parameter for the Entity, you can still use the -Recursion parameter with it to control whether you want to inspect only THAT entity, or its children, or both.

If you will skip -startdate or -enddate make sure you know that function is setting those variable up anyway with default values of: Enddate -> NOW , -startdate 7 days ago.

Make sure you know that by default if you will skip those parameters : systemuser and recursion will be set to true and all . So if you wonder how come you received system events if you were looking for user’s events, mark the -systemuser:$false , if you want to look only for event on entity and do not want to step inside its children use -recursion ‘self’ , because by default i set it to ‘all’.

You do not need to provide entity for this function to start, if not given it searches through everything.

Enjoy

function Get-gEvents
{
  <# .SYNOPSIS Get-gEvents is utilizing EventCollector to get information about events. .DESCRIPTION Get-gEvents utilizies most of what EventCollector has to offer. If Start or End date is not specified, the default values of: End - Now, Start - last 7 days will be applied. If you are using EventUserName parameter, you can then choose SystemUser parameter. By default it is selected as True, so if you do not want to see system events please set it to -SystemUser:$false , so you will get only user's records. This function also allows to get current list of types for events, if you do not remember a particular event, first try this function with -ListMainEVTypes From there you can use -ListSecEVTypes with -SubEVType and give it value of name of typse from previous command. .EXAMPLE Count DRS vmotions in Virtual Center for last day. (Get-gEvents -StartDate (get-date).AddDays(-1) -EventTypeIds 'VmBeingHotMigratedEvent').count .EXAMPLE Get-gEvents -ListSecEVTypes -SubEVType 'VmEvent' .PARAMETER ListMainEVTypes If used , you will received main event types categories. .PARAMETER ListSecEVTypes If used, you have to use SuvEVType to select the right subcategory to expand. .PARAMETER SubEVType From output of -ListMainEVTypes you can paste its name to -SubEVType , in response you will get more detailed event types. for example -SubEVType 'VmEvent' .PARAMETER eventChainId If you are interested only in particular event chain you can specify it's id here. .PARAMETER EventUsername You can search only user's events by giving in this parameter the login of user for example: 'userX', 'DOMAIN\UserX' .PARAMETER EntityName Name of the entity for which or underwhich you will be looking for events. It works together with ViewType. .PARAMETER systemUser In use with EventUsername, By default systemUser is $True, so that you will see the user's and the system events. If you will give -systemUser:$false then, only user's events will be gathered. .PARAMETER EventTypeIds This parameters can handle multiple ids of eventtypes separated by comas, for example : VmBeingHotMigratedEvent for checking drs migrations or VmPoweredOffEvent for vms getting powered off. If you do not know the specific type id, you can always run this function with ListMainEVTypes and ListSecEVTypes paramegters to get hints. .PARAMETER category Possible categories are: info, warning, error, user. .PARAMETER StartDate Start date for the search query. For example (get-date) for now, or (get-date).AddDays(-1) for yesterday. .PARAMETER EndDate End date for the search query. For example (get-date) for now, or (get-date).AddDays(-1) for yesterday. .PARAMETER Recursion You can choose from Self,Children,All options. Self is only checking events on Entity itself, Childred only on Children entities, and all is Self+Children. .PARAMETER ViewType This is the ViewType that you would normally use within Get-View to find that particular entity. For example HostSystem for host, or VirtualMachine for VM, or ClusterComputeResource for Cluster. .LINK https://psvmware.wordpress.com .NOTES Author : Grzegorz Kulikowski #>
	[cmdletbinding(DefaultParametersetName = "Main Usage")]
	param (
		[parameter(Mandatory = $true, ParameterSetName = "Main Events Types Listing")][switch]$ListMainEVTypes,
		[parameter(Mandatory = $true, ParameterSetName = "Detailed SubEvents Types Listing")][switch]$ListSecEVTypes,
		[parameter(Mandatory = $true, ParameterSetName = "Detailed SubEvents Types Listing")]$SubEVType,
		[parameter(Mandatory = $true, ParameterSetName = "ChainID Events Listing")][string]$eventChainId,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][string[]]$EventUsername,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][bool]$systemUser = $true,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][string]$EntityName,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][String[]]$EventTypeIds,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][VMware.Vim.EventCategory]$category,
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][datetime]$StartDate = (Get-Date).AddDays(-7),
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][datetime]$EndDate = (Get-date),
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][vmware.vim.EventFilterSpecRecursionOption]$Recursion = 'all',
		[parameter(Mandatory = $false, ParameterSetName = "Main Usage")][ValidateSet('ClusterComputeResource', 'ComputeResource', 'Datacenter', 'Datastore', 'DistributedVirtualPortgroup', 'DistributedVirtualSwitch', 'Folder', 'HostSystem', 'Network', 'OpaqueNetwork', 'ResourcePool', 'StoragePod', 'VirtualApp', 'VirtualMachine', 'VmwareDistributedVirtualSwitch')][string]$ViewType
	)
	
	switch ($PsCmdlet.ParameterSetName)
	{
		"Main Events Types Listing"  {
			[VMware.Vim.VmEvent].Assembly.GetTypes() | ? { $_.BaseType -eq [VMware.Vim.Event] }
			break
			
		}
		"Detailed SubEvents Types Listing"  {
			
			[VMware.Vim.VmEvent].Assembly.GetTypes() | ? { $_.BaseType -like "VMware.Vim.$SubEVType" }
			break
		}
		"Main Usage" {
			$si = get-view -id ServiceInstance
			$em = get-view -id $si.Content.EventManager
			
			$EventFilterSpec = New-Object VMware.Vim.EventFilterSpec
			$EventFilterSpec.eventTypeId = $EventTypeIds
			if ($StartDate -or $EndDate)
			{
				Write-debug "Date was given"
				$EventFilterSpec.Time = New-Object Vmware.Vim.EventFilterSpecByTime
				$EventFilterSpec.Time.beginTime = $StartDate
				$EventFilterSpec.Time.endTime = $EndDate
			}
			if ($category)
			{
				Write-debug "Category was given"
				$EventFilterSpec.Category = $category
			}
			if ($EventUsername)
			{
				Write-debug "User was given"
				$EventFilterSpec.userName = New-Object Vmware.Vim.EventFilterSpecByUsername
				$EventFilterSpec.userName.systemUser = $systemUser
				$EventFilterSpec.userName.userList = $EventUsername
			}
			if ($EventChainId)
			{
				Write-debug "EventChainId was given"
				$EventFilterSpec.EventChainId = $EventChainId
			}
			if ($EntityName)
			{
				Write-debug "Entity was given"
				$entity = get-view -viewtype $ViewType -Filter @{ 'name' = $EntityName } -Property name
				$EventFilterSpec.Entity = New-Object VMware.Vim.EventFilterSpecByEntity
				$EventFilterSpec.Entity.Entity = $entity.moref
				$EventFilterSpec.Entity.Recursion = $Recursion
			}
			$EventCollector = get-view -id $EM.CreateCollectorForEvents($Eventfilterspec)
			$EventCollector.RewindCollector()
			write-debug "Collector rewinded"
			$events = $null
			
			
			while ($EventInWindow = $EventCollector.ReadNextEvents(100))
			{
				$Events += $EventInWindow
				write-debug "Reading next window"
			}
			$EventCollector.DestroyCollector()
			$Events
			break}
	}
	
	
}

Using SearchIndex in PowerCLI , searching vms by their dns name, ip, and more

Today i want to bring closer something that might not be utilized that often.
SearchIndex
What is SearchIndex and what can it do for us you might ask ? Well, best description is at the source 😉 so

The SearchIndex service allows a client to efficiently query the inventory for a specific managed entity by attributes such as UUID, IP address, DNS name, or datastore path. Such searches typically return a VirtualMachine or a HostSystem. While searching, only objects for which the user has sufficient privileges are considered. The findByInventoryPath and findChild operations only search on entities for which the user has view privileges; all other SearchIndex find operations only search virtual machines and hosts for which the user has read privileges. If the user does not have sufficient privileges for an object that matches the search criteria, that object is not returned.

After connecting to virtual center using PowerCLI, you want to initialize the SearchIndex first:

We start from getting ServiceInstance, from the content we take view of the SearchIndex

$si = get-view -id ServiceInstance
$SearchIndex = Get-view -id $si.Content.SearchIndex

What can do SearchIdex for us ?

$SearchIndex | gm -MemberType 'Method'

TypeName: VMware.Vim.SearchIndex

Name                Member
----                ------
Equals              Method
FindAllByDnsName    Method
FindAllByIp         Method
FindAllByUuid       Method
FindByDatastorePath Method
FindByDnsName       Method
FindByInventoryPath Method
FindByIp            Method
FindByUuid          Method
FindChild           Method

How to get a VM with dns name of X, well you can filter that vm from the all vms by looking at their properties for example. Another example is to use get-view with filter on that hostname property :

get-view -ViewType virtualmachine -filter @{'Guest.Hostname'='vm01.domain.corp'}

And now we are going to utilize SearchIndex to do the same:

$SearchIndex.FindAllByDnsName($null,'vm98.domain.corp',$true)
Type		Value
----		-----
VirtualMachine	vm-8254

In return we have received ManagedObjectReference to a virtualmachine. If we want to see it , we would have to get its view.

get-view -id $SearchIndex.FindAllByDnsName($null,'vm98.domain.corp',$true)

The FindAllByDnsName has 3 arguments, the middle one is obviously the queried dns name. First one is for specifying the datacenter in which you want to look for it. The third, is for choosing the type, when it is set to $true, then we will be looking for a vm with that name, if $false, then we will be looking for a vmhost with that name.
For example if looking up host for specific name:

get-view -id $SearchIndex.FindAllByDnsName($null,'esxi01.domain.corp',$false)  

For the datacenter argument , what is expected is a ManagedObjectReference to a particular datacenter. First we need to get the moref for example of our datacenter:

(get-view -ViewType datacenter -Filter @{'name'='OurDC'}).MoRef

And use it with the SearchIndex FindAllByDnsName method

get-view -id $SearchIndex.FindAllByDnsName((get-view -ViewType datacenter -Filter @{'name'='OurDC'}).MoRef,'ourVM01.domain.corp',$true)

Let’s move forward,
FindByIp

We can use Get-View with proper filter like :

get-view -ViewType VirtualMachine -Filter @{'Guest.IpAddress'='192.168.0.1'}

or utilize SearchIndex for that

$SearchIndex.FindAByIp($null,'192.168.0.1',$true)

arguments:

$SearchIndex.FindByIp
OverloadDefinitions
-------------------
VMware.Vim.ManagedObjectReference[] FindAllByIp(
VMware.Vim.ManagedObjectReference datacenter, 
string ip, 
bool vmSearch
)

First and thrid argument usage is the same as in previous exmaple.

FindByUuid

$SearchIndex.FindByUuid
OverloadDefinitions
-------------------
VMware.Vim.ManagedObjectReference FindByUuid(
VMware.Vim.ManagedObjectReference datacenter, 
string uuid, 
bool vmSearch, 
System.Nullable[bool] instanceUuid
)

For vm searches we set the vmSearch to true, then if instanceUuid is set to true we can search for using its instanceUuid instead of bios uuid. for example:

$SearchIndex.FindByUuid($null,'50394f6f-8408-5340-13e5-0ba59be0c952',$true,$true)
-using VC InstanceUuid
$SearchIndex.FindByUuid($null,'42398a02-5987-483b-211e-2bb7b4ab844f',$true,$false)
-using SMbios Uuid

FindByInventoryPath

$SearchIndex.FindByInventoryPath

OverloadDefinitions
-------------------
VMware.Vim.ManagedObjectReference FindByInventoryPath(string inventoryPath)

Use:

$SearchIndex.FindByInventoryPath('/Datacenter1/vm/Production/vm01')

FindByDatastorePath

$SearchIndex.FindByDatastorePath

OverloadDefinitions
-------------------
VMware.Vim.ManagedObjectReference FindByDatastorePath(VMware.Vim.ManagedObjectReference datacenter, 
string path
)

Use:

$SearchIndex.FindByDatastorePath((get-view -ViewType datacenter -Filter @{'name'='FirstFloorDatacenter'}).MoRef,'[DS09_FastDatastore] VMxyz/VMxyz.vmx')

FindChild

$SearchIndex.FindChild

OverloadDefinitions
-------------------
VMware.Vim.ManagedObjectReference FindChild(
VMware.Vim.ManagedObjectReference entity, 
string name
)

Use:

$cluster = (Get-View -ViewType ClusterComputeResource -Filter @{'name'='ClusterA'}).moref
$SearchIndex.FindChild($x,'esxi01.corp')
-finds hostA in ClusterA

Find CPU hogging vms using PowerCLI

Hello,
this time i would like to show how can we check if we have any vms that are hogging cpu for too long. I bet you are already preventing this using VC alarms. So lets build a simple script that helps us out in getting list of vms that have cpu usage at near 100% for some time.
What we have to do here is:
1) figure out where alarms are triggered , for which vms
2) figure out how to get the root folder of alarms
3) figure out how to get definitions of alarms
4) figure out how to select vms that are using cpu too much for some predefined period of time
5) figure out how to get the cpu stats for vms from point number 4

Let’s go:
Lets first get the service instance object:

$si=get-view serviceinstance

I have decided to put more explanations in this post.
So, serviceinstance, what is that ? I think that at best it is described in the documentation. “The ServiceInstance managed object is the singleton root object of the inventory on both vCenter servers and servers running on standalone host agents. The server creates the ServiceInstance automatically, and also automatically creates the various manager entities that provide services in the virtual environment. Some examples of manager entities are LicenseManager, PerformanceManager, and ViewManager. You can access the manager entities through the content property.”
Make sure to use that link to read service instance description, it will really help to understand how it works.
We will be using alarm manager in order to achieve our goal, let’s have a look how we get there.
sicontent
And ? What to do now ? We see Alarm-AlarmManager. If we would like to return it, we will just get something with Type,Value properties. So how this can help us?
Let’s see what is this by checking the type, and then use get-help on get-view parameter called ID. This should give us some hint what to do next. So This magic Alarm-AlarmManager is actually a moref/ManagedObjectReference , having that we can get its view using get-view.
amn

Then define from when we should build the statistics for vm cpu hogging

$days=-7

Let’s get the VC root folder , and then find out which alarms are triggered there on.

$rootfolderviewalarms=(get-view -id $si.Content.RootFolder).TriggeredAlarmState

Then let’s get the alaram manager object:

$am=get-view -id $si.Content.AlarmManager

Let’s get defined alarms ids in our root vc folder.
We can do this for example using GetAlarm method from our AlarmManager. But..but..but.. how ?! Ok, lets take few steps back. What if we do not know how to do this, or if there even is a method that can do this ? First thing we can do is to inspect the alarm manager, to see what he can do for us.
On the screenshot below, you can see our alarm manager object $am. We want to see what it can do for us, we use get-member to get list of his methods. From there we can see that he has a method called GetAlarm. In order to check what it does we can use the documentation.
From the output of this method, we know that it will be returning morefs(defined alarm ids on particular entity), and in order to use it we need to give it a entity moref, a place where we look for alarms. Now the documentation for GetAlarm method also states that if the entity will not be set, then it will return all visible alarms. If you would like to use it like that, you would have to run it with $null as argument.
amm
In this example we will get only alarms defined on the root of our Virtual Center server.

$alarmids=$am.GetAlarm($si.Content.rootfolder)

Once again, what is it that was returned ? Morefs ! correct. As such they don’t hold too much information. We can get that information though. Using what ? Get-View , connected to that id. Screenshot below shows how to get from ids to actual alarm definition objects with information.
Lets take information about those alarms.

$alarmdefinitions=get-view -id $alarmids

alarmsa1
Let’s find the alarm id that describes the alarm for virtual machine cpu usage.
Now this part is bit tricky. I am making assumption here that we have only 1 alarm defined for vm cpu usage, and that it was defined in the root virtual center folder. This script will not work if you have defined more alarms than 1 for vm cpu usage because i am searching only for alarm system name ‘alarm.VmCPUUsageAlarm’, and i am not filtering by its name. So we are looking at $alarmdefinitions array that holds definitions of alarms with .info object that has a systemname property. We filter it so we can get in result the VM cpu usage alarm, and selecting its alarm moref.

$vmcpuusagealarmid=($alarmdefinitions|Where-Object {$_.Info.Systemname -eq 'alarm.VmCPUUsageAlarm' }).info.alarm
$vmcpuusagealarmid
Type                                              Value
----                                              -----
Alarm                                             alarm-6

So the alarm id that is about vm cpu usage is Alarm-alarm-6.
Let’s get now ids of virtual machines that have currently triggered alarm that we have found previously. Our root VC container had a property called TriggeredAlarmState that holds morefs of entities and alarm ids triggered on those entities. We will now filter them them to get only those that have vm cpu usage alarm triggered.
alarmsa2

$vmswithcpualarms=$rootfolderviewalarms|?{$_.Alarm -eq $vmcpuusagealarmid}

So now we have in $vmswithcpualarms only those triggered alarm states that match our vms with alarm of vm cpu usage.
Let’s change ids to vm view objects, so that we can grab vm names. So far we have only in $vmsswithcpualarms properties called Entity which is only a moref.

$cpuhoggingVMs=get-view -property name -id ($vmswithcpualarms | %{$_.Entity})

Now let’s build statistics from a ‘cpu.usage.average’ metric. We will use 2h intervals and we would like to get data from -7 days until now as stated in $days variable. PowerCLI gives us get-stat cmdlet that we will use. It accepts entity names. I am using $cpuhoggingVMS|%{$_.name} in order to return only names directly. Same as if you would type : -Entity ‘vm1′,’vm2′,’vm3′,’vm4’

$result=Get-Stat -Entity ($cpuhoggingVMS|%{$_.name}) -Start (Get-Date).AddDays($days) -Finish (get-date) -Stat 'cpu.usage.average' -IntervalMins 120

We have now our data stored in $result variable, we have a lot of data there, for each virtual machine statistics about its cpu usage.
Ok what if you say that you can not distinguish from which vm is that statistic data ? I say : “We need to go deeper” 🙂
So gm or get-member on the result entry shows that there are more properties than only those which are displayed.

alarmsa3

And the last line! We will group statistics for the vms by their name, and then for each of them we will measure their average cpu usage during that period of time.

$reportVMcpu=$result | select value,Entity | Group-Object -Property entity | % {$temp=$_; $temp.group | Measure-Object -Property value -average | select @{n='CPU % Average usage';e={[math]::round($_.average,3)}}, @{n='entity';e={$temp.name} }} | Sort-Object -Propert 'CPU % Average usage' -Descending

Now, if we will display our report, we will get a summary of vms and its corresponding average cpu usage through last 7 days in our example.
vmcpuhoggers
We can now tell that some vms here have an average of cpu usage for lat 7 days at 99%, that would indicate that something went bad inside this vm, and we need to investigate it. We do not like vms that hog cpus without any reason for too long 😉
Why would we want this report ?
Ok, i bet you are using alarms for VM cpu usage, and the alarm kicks in after 5..15..30.. minutes for example. You might assume that something went wrong inside the vm, but there are vms that for example are working really hard only during some specific time window. For example systems that are doing calculations at end of the month, or that use cpu only for few days in week/month as per design/function. Each time that alarms is triggered you would have to go to vm performance, and check if this is abnormal situation/call the vm owner/ or look for any pattern in its cpu usage. If you will see that this vm behaves as expected because it is normal to consume that amount of cpu only on Mondays, you would ignore that alarm and just wait to see if alarm gets cleared as previously.

I hope that this post will help you start using alarm manager and other managers, as well as understand morefs and using get-view.

Below is the code without any comments.

$si=get-view serviceinstance
$days=-7
$rootfolderviewalarms=(get-view -id $si.Content.RootFolder).TriggeredAlarmState
$am=get-view -id $si.Content.AlarmManager
$alarmids=$am.GetAlarm($si.Content.rootfolder)
$alarmdefinitions=get-view -id $alarmids
$vmcpuusagealarmid=($alarmdefinitions|Where-Object {$_.Info.Systemname -eq 'alarm.VmCPUUsageAlarm' }).info.alarm
$vmswithcpualarms=$rootfolderviewalarms|?{$_.Alarm -eq $vmcpuusagealarmid}
$cpuhoggingVMs=get-view -property name -id ($vmswithcpualarms | %{$_.Entity})
$result=Get-Stat -Entity ($cpuhoggingVMS|%{$_.name}) -Start (Get-Date).AddDays($days) -Finish (get-date) -Stat 'cpu.usage.average' -IntervalMins 120
$reportVMcpu=$result | select value,Entity | Group-Object -Property entity | % {$temp=$_; $temp.group | Measure-Object -Property value -average | select @{n='CPU % Average usage';e={[math]::round($_.average,3)}}, @{n='entity';e={$temp.name} }} | Sort-Object -Propert 'CPU % Average usage' -Descending

Enjoy!

Get-VMHostTimeReport Reporting time from vmhost system

I wanted to check if all of my vmhost system are keeping time with ntp. I wrote recently how to query time from vmhost system using esxcli. Today i want to show how to do this without esxcli, and also produce nice report and a summary which will help investigate issue with host time. I am comparing host time with local time o/s that is converted to UTC. If you are not sure whether your time is accurate then it might be a problem. Solution for this is to query remote utc time. In internet you can find sites that offer time web services that can be easily integrated to this script.
Have in mind that property DiffToUTC is in seconds.
I have tried to optimize this report as much as i can. It was possible to execute this report in 1 minute and 8 seconds. But it was not that readable and did not contain all of this information. On 100 vmhost+ it should fit in 1 – 2 minutes. Before i had approach with obtaining time using esxcli, but that was slower then this approach.
If you know how to query it faster, post a comment. Have in mind that this function is not perfect. I believe that there are better ways to show weather vmhost system is out of time or not. Anyway, this function helps a lot in diagnosing problems time on esx/esxi host systems.
It will state if ntpd is running or not, or for example if you have >100 host you can spot a pattern for ntp servers. Sometimes it is easy to overlook something, for example ntp servers:
172.16.x.y
72.16.x.y
Where you can easily see that you had a typo.
Sometimes you are just not aware that ntp service is running.
Or sometimes… 😉
Few screenshots from running this function:
From this screenshot where the report was stored in $myreport, we can see that we have some issues with host time, as there is a difference of 76 second compared to my local os time.
srh2
If you see DiffToUTC like 0.0xxxxx that would mean that you are ok. If you get readings > 1 seconds that could indicate something is wrong.
We can also do report for a single vmhost system.
srh1
We can do a full report and using the -summary switch, make function to return some description about the report.
trsum
Documentation for
HostDateTimeSystem -> Managed Object – HostDateTimeSystem
HostServiceSystem -> Managed Object – HostServiceSystem
I have added now option to check your local time which will be converted to UTC and compare it to UTC time taken from internet. Internet connection should be in place in order to use it. That was added if you suspect that your local time might not be correct and you would like to check it with other source. Please also read disclaimer,usage restrictions before using this time web service. So if you spot that your time differs to much from time taken from that ‘internet returned time’, it might indicate some issues, maybe the web service has issues, or our system.
Function below:

function Get-VMHostTimeReport {
<#
	.SYNOPSIS
		Gets time from VM Hosts and checks with local time(to utc).

	.DESCRIPTION
		This function might help investigating issues with vm host time. If it is running without any
		parameter it checks time for all hosts registered in virtual center to which user is currently
		connected. Using parameter SingleVMHost will produce report for single vm host system.
		VMHost should be the name of the host, string.
		Report returns colums : Name (vmhost system name), VMHostTime (Time from vmhost),
		UTCTime (this is the utc time from our local os), NTPServers ( if any are in the vmhost
		configuration), NTPServiceRunning (checks if the ntp service is running on the vmhost),
		DiffToUTC (that's the difference in seconds between time reported by vmhost and our os)
		By default it sorts report by from lowest to highest time difference reported.
		

	.PARAMETER  SingleVMHost
		Specify single vmhost name that is registered in VC. This should be a string.
		
	.PARAMETER  Summary
		Indicate if you would like to receive short summary about produced report.

	.PARAMETER  CheckTimeFromInternet
		Indicate if you would like to see in summary information about your local time and remote 
		time. Time from remote web service will be checked and comapred to your system utc time.

	.EXAMPLE
		PS C:\> Get-VMHostTimeReport
		Will produce report for all vmhosts that are registered within VirtualCenter to which user
		is currently connected. It is possible to close the report into a variable for example:
		$timereport=Get-VMHostTimeReport
		You can then export this report to csv if needed for example, or view it again :
		$timereport | format-table -autosize
		For viewing convenience
		
		
	.EXAMPLE
		PS C:\> Get-VMHostTimeReport -SingleVMHost 'myesxhost.local.lan'
		Will produce report for given vmhost that is registered within VirtualCenter to which user
		is currently connected.
		

	.EXAMPLE
		PS C:\> Get-VMHostTimeReport -Summary
		Will produce report . and show a small summary which might indicate if there is a problem
		with time sync on vmhost systems. Example of the summary below:
		UTC time from the current system :Min and Max times during reporting period
		Min: 9/3/2013 4:17:38 PM
		Max: 9/3/2013 4:19:20 PM
		While function was creating this report, first date that was returned by local os was the Min
		and the last date that was returned by local os was the Max value.
		VMHosts reported Times :Min and Max date / time while creating report
		Min: 9/3/2013 4:16:35 PM
		Max: 9/3/2013 4:19:20 PM
		Our vmhost systems reported their date/time. If this time span is too big, this might indicate 
		issues with vmhost ntp sync.
		Time Difference between VMHost and UTC from local os time Min, Max, Avg
		Min: 0.03075
		Max: 76.20985
		Avg: 1.36604993421053
		This is summary for comparing UTC vm host time to UTC time from local os. If you see 
		big Max value >1/2 sec that it might indicate that there is an issue with vmhost ntp time sync
		If a switch parameter CheckTimeFromInternet is present in the summary section there will
		be small report generated about your local time converted to UTC and time taken from
		http://www.earthtools.org
		You can then quickly see if there is an issue with your local time

	.NOTES
		NAME:  Get-VMHostTimeReport
		
		AUTHOR: Grzegorz Kulikowski
		
		NOT WORKING ? #powercli @ irc.freenode.net 
		
		THANKS: http://www.earthtools.org

	.LINK

https://psvmware.wordpress.com

#>

   param(
   [string]$SingleVMHost,
   [switch]$Summary,
   [switch]$CheckTimeFromInternet
   )
$TimeReport=@()
if ($SingleVMHost) { $VMHosts=Get-View -ViewType HostSystem -Filter @{'name'=$SingleVMHost} }
else{
$VMHosts=get-view -viewtype hostsystem -property name,ConfigManager.DateTimeSystem,ConfigManager.ServiceSystem -Filter @{'runtime.ConnectionState'='connected'}
}
Foreach($VMHost in $VMHosts){
$VMHostDateTimeSystem=get-view -id $VMHost.ConfigManager.DateTimeSystem
$VMHostServiceSystem=get-view -id $VMHost.ConfigManager.ServiceSystem
$VMHostTime=$VMHostDateTimeSystem.QueryDateTime()
$NtpServiceState=($VMHostServiceSystem.ServiceInfo.Service|Where-Object {$_.Key -eq 'ntpd'}).Running
$NtpServers=$VMHostDateTimeSystem.DateTimeInfo.NtpConfig.Server
$UTCTime=(Get-Date).ToUniversalTime() 
$TimeReport+=$vmhost| Select-Object -Property Name, @{n='VMHostTime';e={$VMHostTime}},@{n='UTCTime';e={$UTCTime}},@{n='NTPServers';e={$NtpServers}},@{n='NTPServiceRunning';e={$NtpServiceState}},@{n='DiffToUTC';e={[Math]::Round([math]::abs(($VMHostTime - $UTCTime).TotalSeconds),5)}}
}
if($Summary){
$SummaryUTCTime=$TimeReport|Measure-Object -Property UTCTime -Min -Max
$SummaryVMHostTime=$TimeReport|Measure-Object -Property VMHostTime -Min -Max
$SummaryDiffToUTC=$TimeReport|Measure-Object -Property DiffToUTC -Min -Max -Average
Write-Host "UTC time from the current system :Min and Max date/time while creating this report."
Write-Host "Min: $($SummaryUTCTime.Minimum.ToString())"
Write-Host "Max: $($SummaryUTCTime.Maximum.ToString())"
Write-Host "VMHosts reported Times :Min and Max date/time while creating this report"
Write-Host "Min: $($SummaryVMHostTime.Minimum.ToString())"
Write-Host "Max: $($SummaryVMHostTime.Maximum.ToString())"
Write-Host "Time Difference between VMHost and UTC from local os time Min, Max, Avg"
Write-Host "Min: $($SummaryDiffToUTC.Minimum.ToString())"
Write-Host "Max: $($SummaryDiffToUTC.Maximum.ToString())"
Write-Host "Avg: $($SummaryDiffToUTC.Average.ToString())"
if($CheckTimeFromInternet){
[datetime]$TimeFromInternet=(Invoke-RestMethod -Uri 'http://www.earthtools.org/timezone/52.35000/4.86660').timezone.utctime
$CurrentSystemTimeUTC=(Get-Date).ToUniversalTime()
Write-Host "We took UTC time from internet and compared it to your local time converted to UTC time"
$TimeDiff=[math]::abs(($CurrentSystemTimeUTC-$TimeFromInternet).TotalSeconds)
Write-Host "Reported local time converted to UTC $CurrentSystemTimeUTC"
Write-Host "Reported time taken from internet(http://www.earthtools.org/webservices.htm) $TimeFromInternet"
Write-Host "Difference: $TimeDiff seconds"
}
}
return $TimeReport | Sort-Object -Property DiffToUTC
}