From 586b0b86b5d3cfb262860bbd0cc6e9fdee6bac2f Mon Sep 17 00:00:00 2001 From: MTBoren Date: Tue, 16 Jan 2018 20:43:33 -0500 Subject: [PATCH 1/7] add updates list, begin updates to New-xVM --- powershell/xNew-VM.ps1 | 333 ++++++++++++++++++++++------------------- 1 file changed, 177 insertions(+), 156 deletions(-) diff --git a/powershell/xNew-VM.ps1 b/powershell/xNew-VM.ps1 index 7aaa1960..89aaf045 100644 --- a/powershell/xNew-VM.ps1 +++ b/powershell/xNew-VM.ps1 @@ -1,178 +1,198 @@ -Function xNew-VM { +# Function New-xVM { <# - .SYNOPSIS This script demonstrates a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain + .SYNOPSIS Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain .NOTES Author: William Lam .NOTES Site: www.virtuallyghetto.com .REFERENCE Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html + .NOTES + Updates added Jan 2018 by Matt Boren: + - use approved verb for function name + - add -TrustAllCert Switch parameter to give user option as to whether they want to disable SSL certificate checking, instead of disabling checking regardless + - complete the comment-based help, so that Get-Help will return fully useful help (with examples and whatnot), as expected + - add pipeline support to take values from pipeline + - work on parameter sets so that -ResourcePool _or_ -Cluster + -VMHost are used (but, so that user can do one or the other) + - correct logic around upper-casing the vC UUID value (was reversed) + - correct variable naming (had issues where global-scope variable were used inside of the function instead of the corresponding parameters, like $destVCConn was being used instead of the parameter $destvc) + - return a Task object, so that user can update task to track progress (via their own Get-Task calls) + - optimize array creation code (just assign collection of values to array variable, instead of potentially more resource-expensive array concatenation (which actually create a new, 1-item larger array for every item addition)) + - remove unnecessary -Cluster parameter, since -VMHost is mandatory for clone to different vCenter + - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) + - improve new object creation for use in CloneVM task (creation of fewer additional interim variables) #> param( - [Parameter( - Position=0, - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$sourcevc, - [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$destvc, - [String]$sourcevmname, - [String]$destvmname, - [String]$switchtype, - [String]$datacenter, - [String]$cluster, - [String]$resourcepool, - [String]$datastore, - [String]$vmhost, - [String]$vmnetworks, - [String]$foldername, - [String]$snapshotname, - [Boolean]$poweron, - [Boolean]$uppercaseuuid + # [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$sourcevc, ## get this from SourceVM + [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$destvc, + [String]$sourcevmname, ## Make SourceVM, take object from pipeline, get "source VC from this" + [String]$destvmname, + [String]$switchtype, + [String]$datacenter, + #[String]$cluster, DETERMINE FROM VMHost + [String]$resourcepool, + [String]$datastore, ## add support for datastorecluster object? + [parameter(Mandatory=$true)][String]$vmhost, + [String]$vmnetworks, + [String]$foldername, + [String]$snapshotname, ## get from VM below, instead of just by name in whole vCenter + [Boolean]$poweron, + [Boolean]$uppercaseuuid, + ## Switch: Trust all SSL certificates, valid or otherwise? Essentially, "SkipCertificateCheck". Note: this sets this behavior for the whole of the current PowerShell session, not just for this command. + [Alias("SkipCertificateCheck")][Switch]$TrustAllCert ) - # Retrieve Source VC SSL Thumbprint - $vcurl = "https://" + $destVC -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; + begin { + if ($TrustAllCert) { + ## if this type is not already present in the current PowerShell session, add it + if (-not ("IDontCarePolicy" -as [Type])) { + 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; + } + } +"@ + } ## end if + ## set the CertificatePolicy to essentially skip cert checking by setting the CheckValidationResult() return to always be $true + [System.Net.ServicePointManager]::CertificatePolicy = New-Object IDontCarePolicy + } ## end if + } ## end begin + + process { + # Retrieve Source VC SSL Thumbprint + $vcurl = "https://" + $destVC + # Need to do simple GET connection for this method to work + Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null + $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") + # Get Thumbprint + add colons for a valid Thumbprint + $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' + # Source VM to clone from + $vm_view = Get-View (Get-VM -Server $sourcevc -Name $sourcevmname) -Property Config.Hardware.Device + + # Dest Datastore to clone VM to + $datastore_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Datastore -Server $destVCConn -Name $datastore) + + # Dest VM Folder to clone VM to + $folder_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Folder -Server $destVCConn -Name $foldername) + + # Dest Cluster/ResourcePool to clone VM to + if ($cluster) { + $cluster_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Cluster -Server $destVCConn -Name $cluster) + $resource = $cluster_view.ExtensionData.resourcePool + } else { + $rp_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-ResourcePool -Server $destVCConn -Name $resourcepool) + $resource = $rp_view.ExtensionData.MoRef + } + + # Dest ESXi host to clone VM to + $vmhost_view = (Get-VMHost -Server $destVCConn -Name $vmhost) + + # Find all Etherenet Devices for given VM which + # we will need to change its network at the destination + $vmNetworkAdapters = @() + $devices = $vm_view.Config.Hardware.Device + foreach ($device in $devices) { + if($device -is [VMware.Vim.VirtualEthernetCard]) { + $vmNetworkAdapters += $device } } -"@ - [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy - # Need to do simple GET connection for this method to work - Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null - - $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") - # Get Thumbprint + add colons for a valid Thumbprint - $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' - - # Source VM to clone from - $vm_view = Get-View (Get-VM -Server $sourcevc -Name $sourcevmname) -Property Config.Hardware.Device - - # Dest Datastore to clone VM to - $datastore_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Datastore -Server $destVCConn -Name $datastore) - - # Dest VM Folder to clone VM to - $folder_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Folder -Server $destVCConn -Name $foldername) - - # Dest Cluster/ResourcePool to clone VM to - if($cluster) { - $cluster_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Cluster -Server $destVCConn -Name $cluster) - $resource = $cluster_view.ExtensionData.resourcePool - } else { - $rp_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-ResourcePool -Server $destVCConn -Name $resourcepool) - $resource = $rp_view.ExtensionData.MoRef - } - - # Dest ESXi host to clone VM to - $vmhost_view = (Get-VMHost -Server $destVCConn -Name $vmhost) - - # Find all Etherenet Devices for given VM which - # we will need to change its network at the destination - $vmNetworkAdapters = @() - $devices = $vm_view.Config.Hardware.Device - foreach ($device in $devices) { - if($device -is [VMware.Vim.VirtualEthernetCard]) { - $vmNetworkAdapters += $device + + # Snapshot to clone from + if($snapshotname) { + $snapshot = Get-Snapshot -Server $sourcevc -Name $snapshotname } - } - - # Snapshot to clone from - if($snapshotname) { - $snapshot = Get-Snapshot -Server $sourcevc -Name $snapshotname - } - - # Clone Spec - $spec = New-Object VMware.Vim.VirtualMachineCloneSpec - $spec.PowerOn = $poweron - $spec.Template = $false - $locationSpec = New-Object VMware.Vim.VirtualMachineRelocateSpec - - $locationSpec.datastore = $datastore_view.Id - $locationSpec.host = $vmhost_view.Id - $locationSpec.pool = $resource - $locationSpec.Folder = $folder_view.Id - - # Service Locator for the destination vCenter Server - # regardless if its within same SSO Domain or not - $service = New-Object VMware.Vim.ServiceLocator - $credential = New-Object VMware.Vim.ServiceLocatorNamePassword - $credential.username = $destVCusername - $credential.password = $destVCpassword - $service.credential = $credential - # For some xVC-vMotion, VC's InstanceUUID must be in all caps - # Haven't figured out why, but this flag would allow user to toggle (default=false) - if($uppercaseuuid) { - $service.instanceUuid = $destVCConn.InstanceUuid - } else { - $service.instanceUuid = ($destVCConn.InstanceUuid).ToUpper() - } - $service.sslThumbprint = $destVCThumbprint - $service.url = "https://$destVC" - $locationSpec.service = $service - - # Create VM spec depending if destination networking - # is using Distributed Virtual Switch (VDS) or - # is using Virtual Standard Switch (VSS) - $count = 0 - if($switchtype -eq "vds") { - foreach ($vmNetworkAdapter in $vmNetworkAdapters) { - # New VM Network to assign vNIC - $vmnetworkname = ($vmnetworks -split ",")[$count] - - # Extract Distributed Portgroup required info - $dvpg = Get-VDPortgroup -Server $destvc -Name $vmnetworkname - $vds_uuid = (Get-View $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid - $dvpg_key = $dvpg.ExtensionData.Config.key - - # Device Change spec for VSS portgroup - $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec - $dev.Operation = "edit" - $dev.Device = $vmNetworkAdapter - $dev.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo - $dev.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection - $dev.device.backing.port.switchUuid = $vds_uuid - $dev.device.backing.port.portgroupKey = $dvpg_key - $locationSpec.DeviceChange += $dev - $count++ + + # Clone Spec + $spec = New-Object VMware.Vim.VirtualMachineCloneSpec + $spec.PowerOn = $poweron + $spec.Template = $false + $locationSpec = New-Object VMware.Vim.VirtualMachineRelocateSpec + + $locationSpec.datastore = $datastore_view.Id + $locationSpec.host = $vmhost_view.Id + $locationSpec.pool = $resource + $locationSpec.Folder = $folder_view.Id + + # Service Locator for the destination vCenter Server + # regardless if its within same SSO Domain or not + $service = New-Object VMware.Vim.ServiceLocator + $credential = New-Object VMware.Vim.ServiceLocatorNamePassword + $credential.username = $destVCusername + $credential.password = $destVCpassword + $service.credential = $credential + # For some xVC-vMotion, VC's InstanceUUID must be in all caps + # Haven't figured out why, but this flag would allow user to toggle (default=false) + if($uppercaseuuid) { + $service.instanceUuid = $destVCConn.InstanceUuid + } else { + $service.instanceUuid = ($destVCConn.InstanceUuid).ToUpper() } - } else { - foreach ($vmNetworkAdapter in $vmNetworkAdapters) { - # New VM Network to assign vNIC - $vmnetworkname = ($vmnetworks -split ",")[$count] - - # Device Change spec for VSS portgroup - $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec - $dev.Operation = "edit" - $dev.Device = $vmNetworkAdapter - $dev.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo - $dev.device.backing.deviceName = $vmnetworkname - $locationSpec.DeviceChange += $dev - $count++ + $service.sslThumbprint = $destVCThumbprint + $service.url = "https://$destVC" + $locationSpec.service = $service + + # Create VM spec depending if destination networking + # is using Distributed Virtual Switch (VDS) or + # is using Virtual Standard Switch (VSS) + $count = 0 + if($switchtype -eq "vds") { + foreach ($vmNetworkAdapter in $vmNetworkAdapters) { + # New VM Network to assign vNIC + $vmnetworkname = ($vmnetworks -split ",")[$count] + + # Extract Distributed Portgroup required info + $dvpg = Get-VDPortgroup -Server $destvc -Name $vmnetworkname + $vds_uuid = (Get-View $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid + $dvpg_key = $dvpg.ExtensionData.Config.key + + # Device Change spec for VSS portgroup + $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec + $dev.Operation = "edit" + $dev.Device = $vmNetworkAdapter + $dev.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo + $dev.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection + $dev.device.backing.port.switchUuid = $vds_uuid + $dev.device.backing.port.portgroupKey = $dvpg_key + $locationSpec.DeviceChange += $dev + $count++ + } + } else { + foreach ($vmNetworkAdapter in $vmNetworkAdapters) { + # New VM Network to assign vNIC + $vmnetworkname = ($vmnetworks -split ",")[$count] + + # Device Change spec for VSS portgroup + $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec + $dev.Operation = "edit" + $dev.Device = $vmNetworkAdapter + $dev.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo + $dev.device.backing.deviceName = $vmnetworkname + $locationSpec.DeviceChange += $dev + $count++ + } } - } - $spec.Location = $locationSpec + $spec.Location = $locationSpec - if($snapshot) { - $spec.Snapshot = $snapshot.Id - } + if($snapshot) { + $spec.Snapshot = $snapshot.Id + } - Write-Host "`nCloning $sourcevmname from $sourceVC to $destVC ...`n" + Write-Host "`nCloning $sourcevmname from $sourceVC to $destVC ...`n" - # Issue Cross VC-vMotion - $task = $vm_view.CloneVM_Task($folder_view.Id,$destvmname,$spec) - $task1 = Get-Task -Server $sourceVCConn -Id ("Task-$($task.value)") -} + # Issue Cross VC-vMotion + $task = $vm_view.CloneVM_Task($folder_view.Id,$destvmname,$spec) + $task1 = Get-Task -Server $sourceVCConn -Id ("Task-$($task.value)") + } ## end process +# } ## end function -# Variables that must be defined +<# +# Variables that must be defined (left over from old development) $sourcevmname = "PhotonOS-02" $destvmname= "PhotonOS-02-Clone" @@ -206,3 +226,4 @@ xNew-VM -sourcevc $sourceVCConn -destvc $destVCConn -sourcevmname $sourcevmname # Disconnect from Source/Destination VC Disconnect-VIServer -Server $sourceVCConn -Confirm:$false Disconnect-VIServer -Server $destVCConn -Confirm:$false +#> \ No newline at end of file From 662bb45a912b2cc2ecd05f71b669756baee4acbc Mon Sep 17 00:00:00 2001 From: MTBoren Date: Tue, 16 Jan 2018 21:00:58 -0500 Subject: [PATCH 2/7] get help working --- powershell/xNew-VM.ps1 | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/powershell/xNew-VM.ps1 b/powershell/xNew-VM.ps1 index 89aaf045..56863722 100644 --- a/powershell/xNew-VM.ps1 +++ b/powershell/xNew-VM.ps1 @@ -1,10 +1,14 @@ -# Function New-xVM { - <# - .SYNOPSIS Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain - .NOTES Author: William Lam - .NOTES Site: www.virtuallyghetto.com - .REFERENCE Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html + <# .Description + Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain + + .SYNOPSIS + Cross vCenter Clone Operation across two different vCenter Servers + + .NOTES + Author: William Lam + Site: www.virtuallyghetto.com + Reference: Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html Updates added Jan 2018 by Matt Boren: - use approved verb for function name - add -TrustAllCert Switch parameter to give user option as to whether they want to disable SSL certificate checking, instead of disabling checking regardless @@ -18,6 +22,9 @@ - remove unnecessary -Cluster parameter, since -VMHost is mandatory for clone to different vCenter - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) - improve new object creation for use in CloneVM task (creation of fewer additional interim variables) + - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) + Ideas for other updates: + - add support for specifying destination datastorecluster #> param( @@ -26,13 +33,13 @@ [String]$sourcevmname, ## Make SourceVM, take object from pipeline, get "source VC from this" [String]$destvmname, [String]$switchtype, - [String]$datacenter, + #[String]$datacenter, DETERMINE FROM VMHost #[String]$cluster, DETERMINE FROM VMHost - [String]$resourcepool, - [String]$datastore, ## add support for datastorecluster object? + [String]$resourcepool, ## if not specified, get the resource pool of the cluster + [String]$datastore, [parameter(Mandatory=$true)][String]$vmhost, [String]$vmnetworks, - [String]$foldername, + [String]$foldername, ## default to "Discovered virtual machine" or "vm" [String]$snapshotname, ## get from VM below, instead of just by name in whole vCenter [Boolean]$poweron, [Boolean]$uppercaseuuid, @@ -71,6 +78,7 @@ $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") # Get Thumbprint + add colons for a valid Thumbprint $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' + # Source VM to clone from $vm_view = Get-View (Get-VM -Server $sourcevc -Name $sourcevmname) -Property Config.Hardware.Device From 7e8c86f7e5abc624298b86314fd92028f48b19db Mon Sep 17 00:00:00 2001 From: MTBoren Date: Wed, 17 Jan 2018 08:16:11 -0500 Subject: [PATCH 3/7] add idea --- powershell/xNew-VM.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/powershell/xNew-VM.ps1 b/powershell/xNew-VM.ps1 index 56863722..13e76aac 100644 --- a/powershell/xNew-VM.ps1 +++ b/powershell/xNew-VM.ps1 @@ -25,6 +25,7 @@ - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) Ideas for other updates: - add support for specifying destination datastorecluster + - add support for cloning template #> param( From 2b5bed2e5d47382f3b8657d5fa3d47746155af4f Mon Sep 17 00:00:00 2001 From: MTBoren Date: Wed, 17 Jan 2018 18:04:00 -0500 Subject: [PATCH 4/7] add upgrades to New-xVM function --- powershell/xNew-VM.ps1 | 285 ++++++++++++++++++++--------------------- 1 file changed, 138 insertions(+), 147 deletions(-) diff --git a/powershell/xNew-VM.ps1 b/powershell/xNew-VM.ps1 index 13e76aac..4ee6c910 100644 --- a/powershell/xNew-VM.ps1 +++ b/powershell/xNew-VM.ps1 @@ -1,49 +1,88 @@ +function New-xVM { <# .Description - Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain + Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain. Requires that current PowerCLI session has connections (via Connect-VIServer) to both source and destination vCenters .SYNOPSIS Cross vCenter Clone Operation across two different vCenter Servers - .NOTES Author: William Lam Site: www.virtuallyghetto.com Reference: Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html + Updates added Jan 2018 by Matt Boren: + - improve ease of use: can get going with as few as four (4) parameters to function, now, vs. a dozen or more (by gleaning info from the values of the few mandatory parameters, and reduction in overall parameter count) - use approved verb for function name - add -TrustAllCert Switch parameter to give user option as to whether they want to disable SSL certificate checking, instead of disabling checking regardless - - complete the comment-based help, so that Get-Help will return fully useful help (with examples and whatnot), as expected - - add pipeline support to take values from pipeline - - work on parameter sets so that -ResourcePool _or_ -Cluster + -VMHost are used (but, so that user can do one or the other) - - correct logic around upper-casing the vC UUID value (was reversed) - - correct variable naming (had issues where global-scope variable were used inside of the function instead of the corresponding parameters, like $destVCConn was being used instead of the parameter $destvc) - - return a Task object, so that user can update task to track progress (via their own Get-Task calls) + - take VM from pipeline + - default to source VM name for dest VM name if no dest VM name specified + - simplify cluster/resourcepool parameters: remove need for -Cluster, and set default value for ResourcePool, so as to reduce number of parameters needed + - simplify specifying destination vCenter -- glean this info from the -VMHost object, instead of requiring user to additionally specify the name of the destination vCenter + - simplify Datacenter parameter (remove it): glean this info from the -VMHost object, too + - add pipeline support to take pertinent values from pipeline (SourceVM) - optimize array creation code (just assign collection of values to array variable, instead of potentially more resource-expensive array concatenation (which actually create a new, 1-item larger array for every item addition)) - - remove unnecessary -Cluster parameter, since -VMHost is mandatory for clone to different vCenter - - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) - - improve new object creation for use in CloneVM task (creation of fewer additional interim variables) - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) + - improve new-object creation for use in CloneVM task (creation of fewer additional interim variables, clean up overall syntax) + - correct logic around upper-casing the vC UUID value (was reversed) + - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) + - return a Task object, so that user can update task to track progress (via their own Get-Task calls) + - sort out variable naming in function (had issues where global-scope variables were used inside of the function instead of the corresponding function parameters, like $destVCConn was being used instead of the parameter $destvc) + - add -WhatIf support + - complete the comment-based help, so that Get-Help will return fully useful help (with examples and whatnot), as expected Ideas for other updates: - add support for specifying destination datastorecluster - add support for cloning template - #> + - take objects for values for additional parameters (already did -SourceVM and -VMHost), for increased precision/accuracy (user can pass the source/destination objects directly instead of dealing with "by string", which might introduce issue due to duplicate names, etc.) + - taking destination VPGs by object will allow for removal of -switchtype parameter, too + - remove additional param for UppercaseUuid and just uppercase the vCenter UUID for the operation for success by default, instead of providing possiblity for failure + + .Example + Get-VM myVM -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost0.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore01 -VMNetwork myDestVPG0 + Clone VM "myVM" to the vCenter of the given destination VMHost. Will name the new VM the same as the source, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This also takes the default for destination vSwitch type of "VDS". + + .Example + Get-VM myVM2 -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost2.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore02 -DestinationVMName myNewVM -VMNetwork myDestVPG1, myDestVPG2 -PowerOn:$true -TrustAllCert + Clone VM to the vCenter of the given destination VMHost. Will use the new VM name, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This will trust SSL certificates involved (useful for when self-signed certs are in use, say, in a test environment). This also takes the default for destination vSwitch type of "VDS", connects the new VM's networks adapters to the specified VPGs, and powers on the new VM. + .Outputs + VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl of the Clone task. This Task object "lives" in the _source_ vCenter task list. + #> + [CmdletBinding(SupportsShouldProcess=$true)] + [OutputType([VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl])] param( - # [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$sourcevc, ## get this from SourceVM - [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$destvc, - [String]$sourcevmname, ## Make SourceVM, take object from pipeline, get "source VC from this" - [String]$destvmname, - [String]$switchtype, - #[String]$datacenter, DETERMINE FROM VMHost - #[String]$cluster, DETERMINE FROM VMHost - [String]$resourcepool, ## if not specified, get the resource pool of the cluster - [String]$datastore, - [parameter(Mandatory=$true)][String]$vmhost, + ## The source VM object to be cloned to destination vCenter. The "Source vCenter" property will be gleaned from this object + [parameter(Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Types.VirtualMachine]$SourceVM, + + ## Name to use for new VM created in destination vCenter. If not specified, will use name of source VM + [Alias("destvmname")][String]$DestinationVMName, + + ## The VMHost object from the destination vCenter on which to create the new VM + [parameter(Mandatory=$true)][VMware.VimAutomation.Types.VMHost]$VMHost, + + ## Name of the destination datastore on which to create the new VM + [parameter(Mandatory=$true)][String]$Datastore, + + ## Name of the VM inventory folder in the destination vCenter in which to put the new VM. If not specified, will default to the top-level VM folder in the destination datacenter + [String]$FolderName = "VM", + + ## Name of the destination resource pool to use in the destination cluster. If none specified, will use the default "Resources" resource pool for the parent cluster of the destination VMHost + [String]$ResourcePool = "Resources", + + ## Type of vSwitch where target VM network(s) reside on destination VMHost. One of "VDS", "VSS". Defaults to "VDS" + [ValidateSet("VDS", "VSS")][String]$switchtype = "VDS", + + ## Name(s) of VPG(s) to which to connect new VM's network adapter(s) [String]$vmnetworks, - [String]$foldername, ## default to "Discovered virtual machine" or "vm" - [String]$snapshotname, ## get from VM below, instead of just by name in whole vCenter + + ## Optional: Name of snapshot on source VM from which to clone the new VM, if any + [String]$SnapshotName, + + ## Power-on the new destination VM after creation? By default, new VM will remain powered off [Boolean]$poweron, - [Boolean]$uppercaseuuid, + + ## Convert the vCenter UUID to uppercase for when performing cross vCenter operation? Some vCenters seemingly require the UUID be in uppercase + [Boolean]$UppercaseUuid, + ## Switch: Trust all SSL certificates, valid or otherwise? Essentially, "SkipCertificateCheck". Note: this sets this behavior for the whole of the current PowerShell session, not just for this command. [Alias("SkipCertificateCheck")][Switch]$TrustAllCert ) @@ -72,79 +111,61 @@ } ## end begin process { - # Retrieve Source VC SSL Thumbprint - $vcurl = "https://" + $destVC + ## get the connection object for the destination vCenter server + $oDestinationVIServer = $global:DefaultVIServers | Where-Object {$_.Name -eq $VMHost.Client.Config.Server} + # In the next few lines, retrieve destination VC SSL Thumbprint + $strDestinationVCUrl = "https://$($oDestinationVIServer.Name)" # Need to do simple GET connection for this method to work - Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null - $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") + Invoke-RestMethod -Uri $strDestinationVCUrl -Method Get | Out-Null + $endpoint_request = [System.Net.Webrequest]::Create($strDestinationVCUrl) # Get Thumbprint + add colons for a valid Thumbprint $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' - # Source VM to clone from - $vm_view = Get-View (Get-VM -Server $sourcevc -Name $sourcevmname) -Property Config.Hardware.Device - - # Dest Datastore to clone VM to - $datastore_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Datastore -Server $destVCConn -Name $datastore) - - # Dest VM Folder to clone VM to - $folder_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Folder -Server $destVCConn -Name $foldername) - - # Dest Cluster/ResourcePool to clone VM to - if ($cluster) { - $cluster_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-Cluster -Server $destVCConn -Name $cluster) - $resource = $cluster_view.ExtensionData.resourcePool - } else { - $rp_view = (Get-Datacenter -Server $destVCConn -Name $datacenter | Get-ResourcePool -Server $destVCConn -Name $resourcepool) - $resource = $rp_view.ExtensionData.MoRef - } - - # Dest ESXi host to clone VM to - $vmhost_view = (Get-VMHost -Server $destVCConn -Name $vmhost) - - # Find all Etherenet Devices for given VM which - # we will need to change its network at the destination - $vmNetworkAdapters = @() - $devices = $vm_view.Config.Hardware.Device - foreach ($device in $devices) { - if($device -is [VMware.Vim.VirtualEthernetCard]) { - $vmNetworkAdapters += $device - } - } - - # Snapshot to clone from - if($snapshotname) { - $snapshot = Get-Snapshot -Server $sourcevc -Name $snapshotname - } + # View object for source VM to clone + $vm_view = $SourceVM.ExtensionData + ## source vCenter info (name) + $strSourceVCenter = $SourceVM.Client.Config.Server + + ## Get destination inventory items + # Dest Datastore + $oDestinationDatastore = $VMHost | Get-Datastore -Name $datastore + # Dest VM Folder + $oDestinationFolder = $VMHost | Get-Datacenter | Get-Folder -Name $FolderName + # Dest ResourcePool + $oDestinationResourcePool = $VMHost | Get-Cluster | Get-ResourcePool -Name $ResourcePool + ## name to use for destination VM + $strDestinationVMName = if ($PSBoundParameters.ContainsKey("DestinationVMName")) {$DestinationVMName} else {$SourceVM.Name} + # Snapshot to clone from, if any + if ($PSBoundParameters.ContainsKey("SnapshotName")) {$oSourceSnapshot = $SourceVM | Get-Snapshot -Name $snapshotname} # Clone Spec - $spec = New-Object VMware.Vim.VirtualMachineCloneSpec - $spec.PowerOn = $poweron - $spec.Template = $false - $locationSpec = New-Object VMware.Vim.VirtualMachineRelocateSpec - - $locationSpec.datastore = $datastore_view.Id - $locationSpec.host = $vmhost_view.Id - $locationSpec.pool = $resource - $locationSpec.Folder = $folder_view.Id - - # Service Locator for the destination vCenter Server - # regardless if its within same SSO Domain or not - $service = New-Object VMware.Vim.ServiceLocator - $credential = New-Object VMware.Vim.ServiceLocatorNamePassword - $credential.username = $destVCusername - $credential.password = $destVCpassword - $service.credential = $credential - # For some xVC-vMotion, VC's InstanceUUID must be in all caps - # Haven't figured out why, but this flag would allow user to toggle (default=false) - if($uppercaseuuid) { - $service.instanceUuid = $destVCConn.InstanceUuid - } else { - $service.instanceUuid = ($destVCConn.InstanceUuid).ToUpper() - } - $service.sslThumbprint = $destVCThumbprint - $service.url = "https://$destVC" - $locationSpec.service = $service - + $spec = New-Object -Type VMware.Vim.VirtualMachineCloneSpec -Property @{ + PowerOn = $poweron + Template = $false + } ## end New-Object + + $locationSpec = New-Object -Type VMware.Vim.VirtualMachineRelocateSpec -Property @{ + datastore = $oDestinationDatastore.Id + host = $VMHost.Id + pool = $oDestinationResourcePool.Id + Folder = $oDestinationFolder.Id + # Service Locator for the destination vCenter Server regardless if its within same SSO Domain or not + service = New-Object -Type VMware.Vim.ServiceLocator -Property @{ + credential = New-Object -Type VMware.Vim.ServiceLocatorNamePassword -Property @{ + ## get the username and password for the destination vCenter from the VIServer connection object in the current session + username = $oDestinationVIServer.Client.Config.Username + password = $oDestinationVIServer.Client.Config.Password + } ## end New-Object ServiceLocatorNamePassword + # For some xVC-vMotion, VC's InstanceUUID must be in all caps + # Haven't figured out why. Hoever, this flag would allow user to toggle (default=false) + instanceUuid = if ($UppercaseUuid) {$oDestinationVIServer.InstanceUuid.ToUpper()} else {$oDestinationVIServer.InstanceUuid} + sslThumbprint = $destVCThumbprint + url = $strDestinationVCUrl + } ## end New-Object ServiceLocator + } ## end New-Object VirtualMachineRelocateSpec + + # Find all Ethernet Devices for given VM which we will need to change its network at the destination + $vmNetworkAdapters = $vm_view.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} # Create VM spec depending if destination networking # is using Distributed Virtual Switch (VDS) or # is using Virtual Standard Switch (VSS) @@ -155,7 +176,7 @@ $vmnetworkname = ($vmnetworks -split ",")[$count] # Extract Distributed Portgroup required info - $dvpg = Get-VDPortgroup -Server $destvc -Name $vmnetworkname + $dvpg = Get-VDPortgroup -Server $oDestinationVIServer -Name $vmnetworkname $vds_uuid = (Get-View $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid $dvpg_key = $dvpg.ExtensionData.Config.key @@ -163,13 +184,15 @@ $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec $dev.Operation = "edit" $dev.Device = $vmNetworkAdapter - $dev.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo - $dev.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection - $dev.device.backing.port.switchUuid = $vds_uuid - $dev.device.backing.port.portgroupKey = $dvpg_key + $dev.device.Backing = New-Object -Type VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo -Property @{ + port = New-Object -Type VMware.Vim.DistributedVirtualSwitchPortConnection -Property @{ + switchUuid = $vds_uuid + portgroupKey = $dvpg_key + } ## end new-object DistributedVirtualSwitchPortConnection + } ## end new-object VirtualEthernetCardDistributedVirtualPortBackingInfo $locationSpec.DeviceChange += $dev $count++ - } + } ## end foreach } else { foreach ($vmNetworkAdapter in $vmNetworkAdapters) { # New VM Network to assign vNIC @@ -179,60 +202,28 @@ $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec $dev.Operation = "edit" $dev.Device = $vmNetworkAdapter - $dev.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo - $dev.device.backing.deviceName = $vmnetworkname + $dev.device.backing = New-Object -Type VMware.Vim.VirtualEthernetCardNetworkBackingInfo -Property @{ + deviceName = $vmnetworkname + } ## end new-object VirtualEthernetCardNetworkBackingInfo $locationSpec.DeviceChange += $dev $count++ - } - } + } ## end foreach + } ## end else $spec.Location = $locationSpec - if($snapshot) { - $spec.Snapshot = $snapshot.Id - } + ## add the source Snapshot info, if any + if($oSourceSnapshot) {$spec.Snapshot = $oSourceSnapshot.Id} - Write-Host "`nCloning $sourcevmname from $sourceVC to $destVC ...`n" + $strShouldProcessMsg_Target = "VMHost '$($VMHost.Name)' in destination vCenter '$($oDestinationVIServer.Name)'" + $strShouldProcessMsg_Action = "Create new VM '$strDestinationVMName' from source VM '$($SourceVM.Name)' from vCenter '$strSourceVCenter'" + if ($PSCmdlet.ShouldProcess($strShouldProcessMsg_Target, $strShouldProcessMsg_Action)) { + Write-Verbose -Verbose "Cloning $($SourceVM.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new VM named '$strDestinationVMName'" - # Issue Cross VC-vMotion - $task = $vm_view.CloneVM_Task($folder_view.Id,$destvmname,$spec) - $task1 = Get-Task -Server $sourceVCConn -Id ("Task-$($task.value)") + # Issue Cross VC-vMotion, get Task MoRef back + $task = $vm_view.CloneVM_Task($oDestinationFolder.Id, $strDestinationVMName, $spec) + ## return the Task object so that the user can track task as desired + Get-Task -Server $strSourceVCenter -Id $task + } ## end if } ## end process -# } ## end function - -<# -# Variables that must be defined (left over from old development) - -$sourcevmname = "PhotonOS-02" -$destvmname= "PhotonOS-02-Clone" -$sourceVC = "vcenter65-1.primp-industries.com" -$sourceVCUsername = "administrator@vsphere.local" -$sourceVCPassword = "VMware1!" -$destVC = "vcenter65-3.primp-industries.com" -$destVCUsername = "administrator@vsphere.local" -$destVCpassword = "VMware1!" -$datastorename = "vsanDatastore" -$datacenter = "Datacenter-SiteB" -$cluster = "Santa-Barbara" -$resourcepool = "MyRP" # cluster property not needed if rp is used -$vmhostname = "vesxi65-4.primp-industries.com" -$vmnetworkname = "VM Network" -$foldername = "Discovered virtual machine" -$switchtype = "vss" -$poweron = $false -$snapshotname = "pristine" -$UppercaseUUID = $true - -# Connect to Source/Destination vCenter Server -$sourceVCConn = Connect-VIServer -Server $sourceVC -user $sourceVCUsername -password $sourceVCPassword -$destVCConn = Connect-VIServer -Server $destVC -user $destVCUsername -password $destVCpassword - -xNew-VM -sourcevc $sourceVCConn -destvc $destVCConn -sourcevmname $sourcevmname -destvmname ` - $destvmname -switchtype $switchtype -datacenter $datacenter -cluster $cluster -vmhost ` - $vmhostname -datastore $datastorename -vmnetwork $vmnetworkname -foldername ` - $foldername -poweron $poweron -uppercaseuuid $UppercaseUUID - -# Disconnect from Source/Destination VC -Disconnect-VIServer -Server $sourceVCConn -Confirm:$false -Disconnect-VIServer -Server $destVCConn -Confirm:$false -#> \ No newline at end of file +} ## end function From 7ba6db41beee5fc0f04bc6702ad57fb1ec661301 Mon Sep 17 00:00:00 2001 From: MTBoren Date: Mon, 22 Jan 2018 20:06:45 -0500 Subject: [PATCH 5/7] rename .ps1 to match renamed function --- powershell/{xNew-VM.ps1 => New-xVM.ps1} | 458 ++++++++++++------------ 1 file changed, 229 insertions(+), 229 deletions(-) rename powershell/{xNew-VM.ps1 => New-xVM.ps1} (98%) diff --git a/powershell/xNew-VM.ps1 b/powershell/New-xVM.ps1 similarity index 98% rename from powershell/xNew-VM.ps1 rename to powershell/New-xVM.ps1 index 4ee6c910..2f3fd837 100644 --- a/powershell/xNew-VM.ps1 +++ b/powershell/New-xVM.ps1 @@ -1,229 +1,229 @@ -function New-xVM { - <# .Description - Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain. Requires that current PowerCLI session has connections (via Connect-VIServer) to both source and destination vCenters - - .SYNOPSIS - Cross vCenter Clone Operation across two different vCenter Servers - - .NOTES - Author: William Lam - Site: www.virtuallyghetto.com - Reference: Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html - - Updates added Jan 2018 by Matt Boren: - - improve ease of use: can get going with as few as four (4) parameters to function, now, vs. a dozen or more (by gleaning info from the values of the few mandatory parameters, and reduction in overall parameter count) - - use approved verb for function name - - add -TrustAllCert Switch parameter to give user option as to whether they want to disable SSL certificate checking, instead of disabling checking regardless - - take VM from pipeline - - default to source VM name for dest VM name if no dest VM name specified - - simplify cluster/resourcepool parameters: remove need for -Cluster, and set default value for ResourcePool, so as to reduce number of parameters needed - - simplify specifying destination vCenter -- glean this info from the -VMHost object, instead of requiring user to additionally specify the name of the destination vCenter - - simplify Datacenter parameter (remove it): glean this info from the -VMHost object, too - - add pipeline support to take pertinent values from pipeline (SourceVM) - - optimize array creation code (just assign collection of values to array variable, instead of potentially more resource-expensive array concatenation (which actually create a new, 1-item larger array for every item addition)) - - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) - - improve new-object creation for use in CloneVM task (creation of fewer additional interim variables, clean up overall syntax) - - correct logic around upper-casing the vC UUID value (was reversed) - - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) - - return a Task object, so that user can update task to track progress (via their own Get-Task calls) - - sort out variable naming in function (had issues where global-scope variables were used inside of the function instead of the corresponding function parameters, like $destVCConn was being used instead of the parameter $destvc) - - add -WhatIf support - - complete the comment-based help, so that Get-Help will return fully useful help (with examples and whatnot), as expected - Ideas for other updates: - - add support for specifying destination datastorecluster - - add support for cloning template - - take objects for values for additional parameters (already did -SourceVM and -VMHost), for increased precision/accuracy (user can pass the source/destination objects directly instead of dealing with "by string", which might introduce issue due to duplicate names, etc.) - - taking destination VPGs by object will allow for removal of -switchtype parameter, too - - remove additional param for UppercaseUuid and just uppercase the vCenter UUID for the operation for success by default, instead of providing possiblity for failure - - .Example - Get-VM myVM -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost0.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore01 -VMNetwork myDestVPG0 - Clone VM "myVM" to the vCenter of the given destination VMHost. Will name the new VM the same as the source, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This also takes the default for destination vSwitch type of "VDS". - - .Example - Get-VM myVM2 -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost2.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore02 -DestinationVMName myNewVM -VMNetwork myDestVPG1, myDestVPG2 -PowerOn:$true -TrustAllCert - Clone VM to the vCenter of the given destination VMHost. Will use the new VM name, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This will trust SSL certificates involved (useful for when self-signed certs are in use, say, in a test environment). This also takes the default for destination vSwitch type of "VDS", connects the new VM's networks adapters to the specified VPGs, and powers on the new VM. - - .Outputs - VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl of the Clone task. This Task object "lives" in the _source_ vCenter task list. - #> - [CmdletBinding(SupportsShouldProcess=$true)] - [OutputType([VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl])] - param( - ## The source VM object to be cloned to destination vCenter. The "Source vCenter" property will be gleaned from this object - [parameter(Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Types.VirtualMachine]$SourceVM, - - ## Name to use for new VM created in destination vCenter. If not specified, will use name of source VM - [Alias("destvmname")][String]$DestinationVMName, - - ## The VMHost object from the destination vCenter on which to create the new VM - [parameter(Mandatory=$true)][VMware.VimAutomation.Types.VMHost]$VMHost, - - ## Name of the destination datastore on which to create the new VM - [parameter(Mandatory=$true)][String]$Datastore, - - ## Name of the VM inventory folder in the destination vCenter in which to put the new VM. If not specified, will default to the top-level VM folder in the destination datacenter - [String]$FolderName = "VM", - - ## Name of the destination resource pool to use in the destination cluster. If none specified, will use the default "Resources" resource pool for the parent cluster of the destination VMHost - [String]$ResourcePool = "Resources", - - ## Type of vSwitch where target VM network(s) reside on destination VMHost. One of "VDS", "VSS". Defaults to "VDS" - [ValidateSet("VDS", "VSS")][String]$switchtype = "VDS", - - ## Name(s) of VPG(s) to which to connect new VM's network adapter(s) - [String]$vmnetworks, - - ## Optional: Name of snapshot on source VM from which to clone the new VM, if any - [String]$SnapshotName, - - ## Power-on the new destination VM after creation? By default, new VM will remain powered off - [Boolean]$poweron, - - ## Convert the vCenter UUID to uppercase for when performing cross vCenter operation? Some vCenters seemingly require the UUID be in uppercase - [Boolean]$UppercaseUuid, - - ## Switch: Trust all SSL certificates, valid or otherwise? Essentially, "SkipCertificateCheck". Note: this sets this behavior for the whole of the current PowerShell session, not just for this command. - [Alias("SkipCertificateCheck")][Switch]$TrustAllCert - ) - - begin { - if ($TrustAllCert) { - ## if this type is not already present in the current PowerShell session, add it - if (-not ("IDontCarePolicy" -as [Type])) { - 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; - } - } -"@ - } ## end if - ## set the CertificatePolicy to essentially skip cert checking by setting the CheckValidationResult() return to always be $true - [System.Net.ServicePointManager]::CertificatePolicy = New-Object IDontCarePolicy - } ## end if - } ## end begin - - process { - ## get the connection object for the destination vCenter server - $oDestinationVIServer = $global:DefaultVIServers | Where-Object {$_.Name -eq $VMHost.Client.Config.Server} - # In the next few lines, retrieve destination VC SSL Thumbprint - $strDestinationVCUrl = "https://$($oDestinationVIServer.Name)" - # Need to do simple GET connection for this method to work - Invoke-RestMethod -Uri $strDestinationVCUrl -Method Get | Out-Null - $endpoint_request = [System.Net.Webrequest]::Create($strDestinationVCUrl) - # Get Thumbprint + add colons for a valid Thumbprint - $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' - - # View object for source VM to clone - $vm_view = $SourceVM.ExtensionData - ## source vCenter info (name) - $strSourceVCenter = $SourceVM.Client.Config.Server - - ## Get destination inventory items - # Dest Datastore - $oDestinationDatastore = $VMHost | Get-Datastore -Name $datastore - # Dest VM Folder - $oDestinationFolder = $VMHost | Get-Datacenter | Get-Folder -Name $FolderName - # Dest ResourcePool - $oDestinationResourcePool = $VMHost | Get-Cluster | Get-ResourcePool -Name $ResourcePool - ## name to use for destination VM - $strDestinationVMName = if ($PSBoundParameters.ContainsKey("DestinationVMName")) {$DestinationVMName} else {$SourceVM.Name} - # Snapshot to clone from, if any - if ($PSBoundParameters.ContainsKey("SnapshotName")) {$oSourceSnapshot = $SourceVM | Get-Snapshot -Name $snapshotname} - - # Clone Spec - $spec = New-Object -Type VMware.Vim.VirtualMachineCloneSpec -Property @{ - PowerOn = $poweron - Template = $false - } ## end New-Object - - $locationSpec = New-Object -Type VMware.Vim.VirtualMachineRelocateSpec -Property @{ - datastore = $oDestinationDatastore.Id - host = $VMHost.Id - pool = $oDestinationResourcePool.Id - Folder = $oDestinationFolder.Id - # Service Locator for the destination vCenter Server regardless if its within same SSO Domain or not - service = New-Object -Type VMware.Vim.ServiceLocator -Property @{ - credential = New-Object -Type VMware.Vim.ServiceLocatorNamePassword -Property @{ - ## get the username and password for the destination vCenter from the VIServer connection object in the current session - username = $oDestinationVIServer.Client.Config.Username - password = $oDestinationVIServer.Client.Config.Password - } ## end New-Object ServiceLocatorNamePassword - # For some xVC-vMotion, VC's InstanceUUID must be in all caps - # Haven't figured out why. Hoever, this flag would allow user to toggle (default=false) - instanceUuid = if ($UppercaseUuid) {$oDestinationVIServer.InstanceUuid.ToUpper()} else {$oDestinationVIServer.InstanceUuid} - sslThumbprint = $destVCThumbprint - url = $strDestinationVCUrl - } ## end New-Object ServiceLocator - } ## end New-Object VirtualMachineRelocateSpec - - # Find all Ethernet Devices for given VM which we will need to change its network at the destination - $vmNetworkAdapters = $vm_view.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} - # Create VM spec depending if destination networking - # is using Distributed Virtual Switch (VDS) or - # is using Virtual Standard Switch (VSS) - $count = 0 - if($switchtype -eq "vds") { - foreach ($vmNetworkAdapter in $vmNetworkAdapters) { - # New VM Network to assign vNIC - $vmnetworkname = ($vmnetworks -split ",")[$count] - - # Extract Distributed Portgroup required info - $dvpg = Get-VDPortgroup -Server $oDestinationVIServer -Name $vmnetworkname - $vds_uuid = (Get-View $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid - $dvpg_key = $dvpg.ExtensionData.Config.key - - # Device Change spec for VSS portgroup - $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec - $dev.Operation = "edit" - $dev.Device = $vmNetworkAdapter - $dev.device.Backing = New-Object -Type VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo -Property @{ - port = New-Object -Type VMware.Vim.DistributedVirtualSwitchPortConnection -Property @{ - switchUuid = $vds_uuid - portgroupKey = $dvpg_key - } ## end new-object DistributedVirtualSwitchPortConnection - } ## end new-object VirtualEthernetCardDistributedVirtualPortBackingInfo - $locationSpec.DeviceChange += $dev - $count++ - } ## end foreach - } else { - foreach ($vmNetworkAdapter in $vmNetworkAdapters) { - # New VM Network to assign vNIC - $vmnetworkname = ($vmnetworks -split ",")[$count] - - # Device Change spec for VSS portgroup - $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec - $dev.Operation = "edit" - $dev.Device = $vmNetworkAdapter - $dev.device.backing = New-Object -Type VMware.Vim.VirtualEthernetCardNetworkBackingInfo -Property @{ - deviceName = $vmnetworkname - } ## end new-object VirtualEthernetCardNetworkBackingInfo - $locationSpec.DeviceChange += $dev - $count++ - } ## end foreach - } ## end else - - $spec.Location = $locationSpec - - ## add the source Snapshot info, if any - if($oSourceSnapshot) {$spec.Snapshot = $oSourceSnapshot.Id} - - $strShouldProcessMsg_Target = "VMHost '$($VMHost.Name)' in destination vCenter '$($oDestinationVIServer.Name)'" - $strShouldProcessMsg_Action = "Create new VM '$strDestinationVMName' from source VM '$($SourceVM.Name)' from vCenter '$strSourceVCenter'" - if ($PSCmdlet.ShouldProcess($strShouldProcessMsg_Target, $strShouldProcessMsg_Action)) { - Write-Verbose -Verbose "Cloning $($SourceVM.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new VM named '$strDestinationVMName'" - - # Issue Cross VC-vMotion, get Task MoRef back - $task = $vm_view.CloneVM_Task($oDestinationFolder.Id, $strDestinationVMName, $spec) - ## return the Task object so that the user can track task as desired - Get-Task -Server $strSourceVCenter -Id $task - } ## end if - } ## end process -} ## end function +function New-xVM { + <# .Description + Perform a Cross vCenter Clone Operation across two different vCenter Servers which can either be part of the same or different SSO Domain. Requires that current PowerCLI session has connections (via Connect-VIServer) to both source and destination vCenters + + .SYNOPSIS + Cross vCenter Clone Operation across two different vCenter Servers + + .NOTES + Author: William Lam + Site: www.virtuallyghetto.com + Reference: Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html + + Updates added Jan 2018 by Matt Boren: + - improve ease of use: can get going with as few as four (4) parameters to function, now, vs. a dozen or more (by gleaning info from the values of the few mandatory parameters, and reduction in overall parameter count) + - use approved verb for function name + - add -TrustAllCert Switch parameter to give user option as to whether they want to disable SSL certificate checking, instead of disabling checking regardless + - take VM from pipeline + - default to source VM name for dest VM name if no dest VM name specified + - simplify cluster/resourcepool parameters: remove need for -Cluster, and set default value for ResourcePool, so as to reduce number of parameters needed + - simplify specifying destination vCenter -- glean this info from the -VMHost object, instead of requiring user to additionally specify the name of the destination vCenter + - simplify Datacenter parameter (remove it): glean this info from the -VMHost object, too + - add pipeline support to take pertinent values from pipeline (SourceVM) + - optimize array creation code (just assign collection of values to array variable, instead of potentially more resource-expensive array concatenation (which actually create a new, 1-item larger array for every item addition)) + - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) + - improve new-object creation for use in CloneVM task (creation of fewer additional interim variables, clean up overall syntax) + - correct logic around upper-casing the vC UUID value (was reversed) + - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) + - return a Task object, so that user can update task to track progress (via their own Get-Task calls) + - sort out variable naming in function (had issues where global-scope variables were used inside of the function instead of the corresponding function parameters, like $destVCConn was being used instead of the parameter $destvc) + - add -WhatIf support + - complete the comment-based help, so that Get-Help will return fully useful help (with examples and whatnot), as expected + Ideas for other updates: + - add support for specifying destination datastorecluster + - add support for cloning template + - take objects for values for additional parameters (already did -SourceVM and -VMHost), for increased precision/accuracy (user can pass the source/destination objects directly instead of dealing with "by string", which might introduce issue due to duplicate names, etc.) + - taking destination VPGs by object will allow for removal of -switchtype parameter, too + - remove additional param for UppercaseUuid and just uppercase the vCenter UUID for the operation for success by default, instead of providing possiblity for failure + + .Example + Get-VM myVM -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost0.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore01 -VMNetwork myDestVPG0 + Clone VM "myVM" to the vCenter of the given destination VMHost. Will name the new VM the same as the source, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This also takes the default for destination vSwitch type of "VDS". + + .Example + Get-VM myVM2 -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost2.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore02 -DestinationVMName myNewVM -VMNetwork myDestVPG1, myDestVPG2 -PowerOn:$true -TrustAllCert + Clone VM to the vCenter of the given destination VMHost. Will use the new VM name, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This will trust SSL certificates involved (useful for when self-signed certs are in use, say, in a test environment). This also takes the default for destination vSwitch type of "VDS", connects the new VM's networks adapters to the specified VPGs, and powers on the new VM. + + .Outputs + VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl of the Clone task. This Task object "lives" in the _source_ vCenter task list. + #> + [CmdletBinding(SupportsShouldProcess=$true)] + [OutputType([VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl])] + param( + ## The source VM object to be cloned to destination vCenter. The "Source vCenter" property will be gleaned from this object + [parameter(Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Types.VirtualMachine]$SourceVM, + + ## Name to use for new VM created in destination vCenter. If not specified, will use name of source VM + [Alias("destvmname")][String]$DestinationVMName, + + ## The VMHost object from the destination vCenter on which to create the new VM + [parameter(Mandatory=$true)][VMware.VimAutomation.Types.VMHost]$VMHost, + + ## Name of the destination datastore on which to create the new VM + [parameter(Mandatory=$true)][String]$Datastore, + + ## Name of the VM inventory folder in the destination vCenter in which to put the new VM. If not specified, will default to the top-level VM folder in the destination datacenter + [String]$FolderName = "VM", + + ## Name of the destination resource pool to use in the destination cluster. If none specified, will use the default "Resources" resource pool for the parent cluster of the destination VMHost + [String]$ResourcePool = "Resources", + + ## Type of vSwitch where target VM network(s) reside on destination VMHost. One of "VDS", "VSS". Defaults to "VDS" + [ValidateSet("VDS", "VSS")][String]$switchtype = "VDS", + + ## Name(s) of VPG(s) to which to connect new VM's network adapter(s) + [String]$vmnetworks, + + ## Optional: Name of snapshot on source VM from which to clone the new VM, if any + [String]$SnapshotName, + + ## Power-on the new destination VM after creation? By default, new VM will remain powered off + [Boolean]$poweron, + + ## Convert the vCenter UUID to uppercase for when performing cross vCenter operation? Some vCenters seemingly require the UUID be in uppercase + [Boolean]$UppercaseUuid, + + ## Switch: Trust all SSL certificates, valid or otherwise? Essentially, "SkipCertificateCheck". Note: this sets this behavior for the whole of the current PowerShell session, not just for this command. + [Alias("SkipCertificateCheck")][Switch]$TrustAllCert + ) + + begin { + if ($TrustAllCert) { + ## if this type is not already present in the current PowerShell session, add it + if (-not ("IDontCarePolicy" -as [Type])) { + 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; + } + } +"@ + } ## end if + ## set the CertificatePolicy to essentially skip cert checking by setting the CheckValidationResult() return to always be $true + [System.Net.ServicePointManager]::CertificatePolicy = New-Object IDontCarePolicy + } ## end if + } ## end begin + + process { + ## get the connection object for the destination vCenter server + $oDestinationVIServer = $global:DefaultVIServers | Where-Object {$_.Name -eq $VMHost.Client.Config.Server} + # In the next few lines, retrieve destination VC SSL Thumbprint + $strDestinationVCUrl = "https://$($oDestinationVIServer.Name)" + # Need to do simple GET connection for this method to work + Invoke-RestMethod -Uri $strDestinationVCUrl -Method Get | Out-Null + $endpoint_request = [System.Net.Webrequest]::Create($strDestinationVCUrl) + # Get Thumbprint + add colons for a valid Thumbprint + $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' + + # View object for source VM to clone + $vm_view = $SourceVM.ExtensionData + ## source vCenter info (name) + $strSourceVCenter = $SourceVM.Client.Config.Server + + ## Get destination inventory items + # Dest Datastore + $oDestinationDatastore = $VMHost | Get-Datastore -Name $datastore + # Dest VM Folder + $oDestinationFolder = $VMHost | Get-Datacenter | Get-Folder -Name $FolderName + # Dest ResourcePool + $oDestinationResourcePool = $VMHost | Get-Cluster | Get-ResourcePool -Name $ResourcePool + ## name to use for destination VM + $strDestinationVMName = if ($PSBoundParameters.ContainsKey("DestinationVMName")) {$DestinationVMName} else {$SourceVM.Name} + # Snapshot to clone from, if any + if ($PSBoundParameters.ContainsKey("SnapshotName")) {$oSourceSnapshot = $SourceVM | Get-Snapshot -Name $snapshotname} + + # Clone Spec + $spec = New-Object -Type VMware.Vim.VirtualMachineCloneSpec -Property @{ + PowerOn = $poweron + Template = $false + } ## end New-Object + + $locationSpec = New-Object -Type VMware.Vim.VirtualMachineRelocateSpec -Property @{ + datastore = $oDestinationDatastore.Id + host = $VMHost.Id + pool = $oDestinationResourcePool.Id + Folder = $oDestinationFolder.Id + # Service Locator for the destination vCenter Server regardless if its within same SSO Domain or not + service = New-Object -Type VMware.Vim.ServiceLocator -Property @{ + credential = New-Object -Type VMware.Vim.ServiceLocatorNamePassword -Property @{ + ## get the username and password for the destination vCenter from the VIServer connection object in the current session + username = $oDestinationVIServer.Client.Config.Username + password = $oDestinationVIServer.Client.Config.Password + } ## end New-Object ServiceLocatorNamePassword + # For some xVC-vMotion, VC's InstanceUUID must be in all caps + # Haven't figured out why. Hoever, this flag would allow user to toggle (default=false) + instanceUuid = if ($UppercaseUuid) {$oDestinationVIServer.InstanceUuid.ToUpper()} else {$oDestinationVIServer.InstanceUuid} + sslThumbprint = $destVCThumbprint + url = $strDestinationVCUrl + } ## end New-Object ServiceLocator + } ## end New-Object VirtualMachineRelocateSpec + + # Find all Ethernet Devices for given VM which we will need to change its network at the destination + $vmNetworkAdapters = $vm_view.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} + # Create VM spec depending if destination networking + # is using Distributed Virtual Switch (VDS) or + # is using Virtual Standard Switch (VSS) + $count = 0 + if($switchtype -eq "vds") { + foreach ($vmNetworkAdapter in $vmNetworkAdapters) { + # New VM Network to assign vNIC + $vmnetworkname = ($vmnetworks -split ",")[$count] + + # Extract Distributed Portgroup required info + $dvpg = Get-VDPortgroup -Server $oDestinationVIServer -Name $vmnetworkname + $vds_uuid = (Get-View $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid + $dvpg_key = $dvpg.ExtensionData.Config.key + + # Device Change spec for VSS portgroup + $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec + $dev.Operation = "edit" + $dev.Device = $vmNetworkAdapter + $dev.device.Backing = New-Object -Type VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo -Property @{ + port = New-Object -Type VMware.Vim.DistributedVirtualSwitchPortConnection -Property @{ + switchUuid = $vds_uuid + portgroupKey = $dvpg_key + } ## end new-object DistributedVirtualSwitchPortConnection + } ## end new-object VirtualEthernetCardDistributedVirtualPortBackingInfo + $locationSpec.DeviceChange += $dev + $count++ + } ## end foreach + } else { + foreach ($vmNetworkAdapter in $vmNetworkAdapters) { + # New VM Network to assign vNIC + $vmnetworkname = ($vmnetworks -split ",")[$count] + + # Device Change spec for VSS portgroup + $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec + $dev.Operation = "edit" + $dev.Device = $vmNetworkAdapter + $dev.device.backing = New-Object -Type VMware.Vim.VirtualEthernetCardNetworkBackingInfo -Property @{ + deviceName = $vmnetworkname + } ## end new-object VirtualEthernetCardNetworkBackingInfo + $locationSpec.DeviceChange += $dev + $count++ + } ## end foreach + } ## end else + + $spec.Location = $locationSpec + + ## add the source Snapshot info, if any + if($oSourceSnapshot) {$spec.Snapshot = $oSourceSnapshot.Id} + + $strShouldProcessMsg_Target = "VMHost '$($VMHost.Name)' in destination vCenter '$($oDestinationVIServer.Name)'" + $strShouldProcessMsg_Action = "Create new VM '$strDestinationVMName' from source VM '$($SourceVM.Name)' from vCenter '$strSourceVCenter'" + if ($PSCmdlet.ShouldProcess($strShouldProcessMsg_Target, $strShouldProcessMsg_Action)) { + Write-Verbose -Verbose "Cloning $($SourceVM.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new VM named '$strDestinationVMName'" + + # Issue Cross VC-vMotion, get Task MoRef back + $task = $vm_view.CloneVM_Task($oDestinationFolder.Id, $strDestinationVMName, $spec) + ## return the Task object so that the user can track task as desired + Get-Task -Server $strSourceVCenter -Id $task + } ## end if + } ## end process +} ## end function From d434d66dfbc46bb9974fc82b55b20ab37999d740 Mon Sep 17 00:00:00 2001 From: MTBoren Date: Mon, 26 Nov 2018 16:30:33 -0500 Subject: [PATCH 6/7] fix credentials need that came with improvement made by removal of Client property in newer PowerCLI --- powershell/New-xVM.ps1 | 88 ++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/powershell/New-xVM.ps1 b/powershell/New-xVM.ps1 index 2f3fd837..0e417649 100644 --- a/powershell/New-xVM.ps1 +++ b/powershell/New-xVM.ps1 @@ -9,8 +9,9 @@ function New-xVM { Author: William Lam Site: www.virtuallyghetto.com Reference: Blog: http://www.virtuallyghetto.com/2018/01/cross-vcenter-clone-with-vsphere-6-0.html - - Updates added Jan 2018 by Matt Boren: + + Updates added by Matt Boren: + Jan 2018: - improve ease of use: can get going with as few as four (4) parameters to function, now, vs. a dozen or more (by gleaning info from the values of the few mandatory parameters, and reduction in overall parameter count) - use approved verb for function name - add -TrustAllCert Switch parameter to give user option as to whether they want to disable SSL certificate checking, instead of disabling checking regardless @@ -18,10 +19,10 @@ function New-xVM { - default to source VM name for dest VM name if no dest VM name specified - simplify cluster/resourcepool parameters: remove need for -Cluster, and set default value for ResourcePool, so as to reduce number of parameters needed - simplify specifying destination vCenter -- glean this info from the -VMHost object, instead of requiring user to additionally specify the name of the destination vCenter - - simplify Datacenter parameter (remove it): glean this info from the -VMHost object, too + - simplify Datacenter parameter (remove it): glean this info from the -VMHost object, too - add pipeline support to take pertinent values from pipeline (SourceVM) - optimize array creation code (just assign collection of values to array variable, instead of potentially more resource-expensive array concatenation (which actually create a new, 1-item larger array for every item addition)) - - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) + - get items for credential for ServiceLocatorNamePassword from destination vCenter connection object (Client.Config) -- Now unsupported (PowerCLI update removed that security loophole -- hurray for better security!) - improve new-object creation for use in CloneVM task (creation of fewer additional interim variables, clean up overall syntax) - correct logic around upper-casing the vC UUID value (was reversed) - improve snapshot selection (limit scope to source VM, instead of "get snapshot by name in all of source vC", which may return multiple snapshots) @@ -29,21 +30,31 @@ function New-xVM { - sort out variable naming in function (had issues where global-scope variables were used inside of the function instead of the corresponding function parameters, like $destVCConn was being used instead of the parameter $destvc) - add -WhatIf support - complete the comment-based help, so that Get-Help will return fully useful help (with examples and whatnot), as expected + Nov 2018: + - take destination vCenter credentials as PSCredential parameter object (previous PowerCLI / API security loophole closed -- yesss!) + - add switch parameter to allow for setting of newly cloned machine to template Ideas for other updates: - add support for specifying destination datastorecluster - - add support for cloning template + - get destination vCenter creds/SAML token in some way, so that user does not have to specify them (presumably already have a connection to destination vCenter, so should not be a need to specify creds for that vCenter again) - take objects for values for additional parameters (already did -SourceVM and -VMHost), for increased precision/accuracy (user can pass the source/destination objects directly instead of dealing with "by string", which might introduce issue due to duplicate names, etc.) - taking destination VPGs by object will allow for removal of -switchtype parameter, too - remove additional param for UppercaseUuid and just uppercase the vCenter UUID for the operation for success by default, instead of providing possiblity for failure + - make destination datastore, VPG parameters optional; if not specified, will just use destination datastore with most freespace and first VPG found, respectively, by default + - add support for cloning from template, too (not just from VM) + - separate -PowerOn and -MarkAsTemplate parameters into separate parametersets, so that user cannot specify both (which can lead to problematic invocations) .Example - Get-VM myVM -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost0.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore01 -VMNetwork myDestVPG0 + Get-VM myVM -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost0.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore01 -VMNetwork myDestVPG0 -Credential $myDestVCCred Clone VM "myVM" to the vCenter of the given destination VMHost. Will name the new VM the same as the source, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This also takes the default for destination vSwitch type of "VDS". .Example - Get-VM myVM2 -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost2.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore02 -DestinationVMName myNewVM -VMNetwork myDestVPG1, myDestVPG2 -PowerOn:$true -TrustAllCert + Get-VM myVM2 -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost2.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore02 -DestinationVMName myNewVM -VMNetwork myDestVPG1, myDestVPG2 -PowerOn -TrustAllCert -Credential $myDestVCCred Clone VM to the vCenter of the given destination VMHost. Will use the new VM name, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This will trust SSL certificates involved (useful for when self-signed certs are in use, say, in a test environment). This also takes the default for destination vSwitch type of "VDS", connects the new VM's networks adapters to the specified VPGs, and powers on the new VM. + .Example + Get-Template myTemplate -Server $mySourceVC | New-xVM -VMHost (Get-VMHost mydesthost0.dom.com -Server myDestVCenter.dom.com) -Datastore destDstore01 -VMNetwork myDestVPG0 -Credential $myDestVCCred -MarkAsTemplate + Clone VM "myVM" to the vCenter of the given destination VMHost. Will name the new VM the same as the source, placing it on the given VMHost and datastore, and in the default "Resources" resource pool in the destination VMHost's parent cluster. This also takes the default for destination vSwitch type of "VDS". Marks as template after clone operation + .Outputs VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl of the Clone task. This Task object "lives" in the _source_ vCenter task list. #> @@ -51,7 +62,10 @@ function New-xVM { [OutputType([VMware.VimAutomation.ViCore.Impl.V1.Task.TaskImpl])] param( ## The source VM object to be cloned to destination vCenter. The "Source vCenter" property will be gleaned from this object - [parameter(Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Types.VirtualMachine]$SourceVM, + [parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="FromVM")][VMware.VimAutomation.Types.VirtualMachine]$SourceVM, + + ## The source template object to be cloned to destination vCenter. The "Source vCenter" property will be gleaned from this object + [parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="FromTemplate")][VMware.VimAutomation.Types.Template]$SourceTemplate, ## Name to use for new VM created in destination vCenter. If not specified, will use name of source VM [Alias("destvmname")][String]$DestinationVMName, @@ -59,6 +73,9 @@ function New-xVM { ## The VMHost object from the destination vCenter on which to create the new VM [parameter(Mandatory=$true)][VMware.VimAutomation.Types.VMHost]$VMHost, + ## Credentials to use for API connection to destination vCenter. Could previously be gleaned from destination VMHost object, but PowerCLI security changes (for the better) no longer support such gleaning, so we need to specify these credentials here. + [parameter(Mandatory=$true)][System.Management.Automation.PSCredential]$CredentialForDestinationVCenter, + ## Name of the destination datastore on which to create the new VM [parameter(Mandatory=$true)][String]$Datastore, @@ -71,20 +88,23 @@ function New-xVM { ## Type of vSwitch where target VM network(s) reside on destination VMHost. One of "VDS", "VSS". Defaults to "VDS" [ValidateSet("VDS", "VSS")][String]$switchtype = "VDS", - ## Name(s) of VPG(s) to which to connect new VM's network adapter(s) - [String]$vmnetworks, + ## Name(s) of VPG(s) to which to connect new VM's network adapter(s). First VM network adapter will be connected to first VMNetwork name, the second to the second, and so on + [String[]]$VMNetwork, ## Optional: Name of snapshot on source VM from which to clone the new VM, if any [String]$SnapshotName, - ## Power-on the new destination VM after creation? By default, new VM will remain powered off - [Boolean]$poweron, - ## Convert the vCenter UUID to uppercase for when performing cross vCenter operation? Some vCenters seemingly require the UUID be in uppercase [Boolean]$UppercaseUuid, ## Switch: Trust all SSL certificates, valid or otherwise? Essentially, "SkipCertificateCheck". Note: this sets this behavior for the whole of the current PowerShell session, not just for this command. - [Alias("SkipCertificateCheck")][Switch]$TrustAllCert + [Alias("SkipCertificateCheck")][Switch]$TrustAllCert, + + ## Switch: Power-on the new destination VM after creation? By default, new VM will remain powered off. If marking new machine as template via -MarkAsTemplate, do not use -PowerOn, of course + [Switch]$PowerOn, + + ## Switch: Specifies whether or not the new virtual machine should be marked as a template. If marking new machine as template, do not use -PowerOn, of course + [Switch]$MarkAsTemplate ) begin { @@ -112,19 +132,24 @@ function New-xVM { process { ## get the connection object for the destination vCenter server - $oDestinationVIServer = $global:DefaultVIServers | Where-Object {$_.Name -eq $VMHost.Client.Config.Server} + $strDestinationVMHostVcenterName = ([System.Uri]$VMHost.ExtensionData.Client.ServiceUrl).DnsSafeHost + $oDestinationVIServer = $global:DefaultVIServers | Where-Object {$_.Name -eq $strDestinationVMHostVcenterName} # In the next few lines, retrieve destination VC SSL Thumbprint - $strDestinationVCUrl = "https://$($oDestinationVIServer.Name)" + $strDestinationVCUrl = "https://$strDestinationVMHostVcenterName" # Need to do simple GET connection for this method to work Invoke-RestMethod -Uri $strDestinationVCUrl -Method Get | Out-Null $endpoint_request = [System.Net.Webrequest]::Create($strDestinationVCUrl) # Get Thumbprint + add colons for a valid Thumbprint $destVCThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' - # View object for source VM to clone - $vm_view = $SourceVM.ExtensionData + # View object for source machine to clone + $viewSourceMachine = Switch ($PSCmdlet.ParameterSetName) { + "FromVM" {$SourceVM.ExtensionData} + "FromTemplate" {$bCloneFromTemplate = $true; $SourceTemplate.ExtensionData} + } ## end switch + ## source vCenter info (name) - $strSourceVCenter = $SourceVM.Client.Config.Server + $strSourceVCenter = ([System.Uri]$viewSourceMachine.Client.ServiceUrl).DnsSafeHost ## Get destination inventory items # Dest Datastore @@ -140,8 +165,8 @@ function New-xVM { # Clone Spec $spec = New-Object -Type VMware.Vim.VirtualMachineCloneSpec -Property @{ - PowerOn = $poweron - Template = $false + PowerOn = $PowerOn.ToBool() + Template = $MarkAsTemplate.ToBool() } ## end New-Object $locationSpec = New-Object -Type VMware.Vim.VirtualMachineRelocateSpec -Property @{ @@ -153,9 +178,12 @@ function New-xVM { service = New-Object -Type VMware.Vim.ServiceLocator -Property @{ credential = New-Object -Type VMware.Vim.ServiceLocatorNamePassword -Property @{ ## get the username and password for the destination vCenter from the VIServer connection object in the current session - username = $oDestinationVIServer.Client.Config.Username - password = $oDestinationVIServer.Client.Config.Password + username = $CredentialForDestinationVCenter.UserName + password = $CredentialForDestinationVCenter.GetNetworkCredential().Password } ## end New-Object ServiceLocatorNamePassword + # credential = New-Object -Type VMware.Vim.ServiceLocatorSAMLCredential -Property @{ + # token = $oDestinationVIServer.SessionSecret + # } ## end New-Object ServiceLocatorSAMLCredential # For some xVC-vMotion, VC's InstanceUUID must be in all caps # Haven't figured out why. Hoever, this flag would allow user to toggle (default=false) instanceUuid = if ($UppercaseUuid) {$oDestinationVIServer.InstanceUuid.ToUpper()} else {$oDestinationVIServer.InstanceUuid} @@ -165,7 +193,7 @@ function New-xVM { } ## end New-Object VirtualMachineRelocateSpec # Find all Ethernet Devices for given VM which we will need to change its network at the destination - $vmNetworkAdapters = $vm_view.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} + $vmNetworkAdapters = $viewSourceMachine.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} # Create VM spec depending if destination networking # is using Distributed Virtual Switch (VDS) or # is using Virtual Standard Switch (VSS) @@ -173,7 +201,7 @@ function New-xVM { if($switchtype -eq "vds") { foreach ($vmNetworkAdapter in $vmNetworkAdapters) { # New VM Network to assign vNIC - $vmnetworkname = ($vmnetworks -split ",")[$count] + $vmnetworkname = $VMNetwork | Select-Object -First 1 -Skip $count # Extract Distributed Portgroup required info $dvpg = Get-VDPortgroup -Server $oDestinationVIServer -Name $vmnetworkname @@ -196,7 +224,7 @@ function New-xVM { } else { foreach ($vmNetworkAdapter in $vmNetworkAdapters) { # New VM Network to assign vNIC - $vmnetworkname = ($vmnetworks -split ",")[$count] + $vmnetworkname = $VMNetwork | Select-Object -First 1 -Skip $count # Device Change spec for VSS portgroup $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec @@ -216,12 +244,12 @@ function New-xVM { if($oSourceSnapshot) {$spec.Snapshot = $oSourceSnapshot.Id} $strShouldProcessMsg_Target = "VMHost '$($VMHost.Name)' in destination vCenter '$($oDestinationVIServer.Name)'" - $strShouldProcessMsg_Action = "Create new VM '$strDestinationVMName' from source VM '$($SourceVM.Name)' from vCenter '$strSourceVCenter'" + $strShouldProcessMsg_Action = "Create new {0} '$strDestinationVMName' from source {1} '$($SourceVM.Name)' from vCenter '$strSourceVCenter'" -f $(if ($MarkAsTemplate) {"template"} else {"VM"}), $(if ($bCloneFromTemplate) {"template"} else {"VM"}) if ($PSCmdlet.ShouldProcess($strShouldProcessMsg_Target, $strShouldProcessMsg_Action)) { - Write-Verbose -Verbose "Cloning $($SourceVM.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new VM named '$strDestinationVMName'" + Write-Verbose -Verbose ("Cloning $($SourceVM.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new {0} named '$strDestinationVMName'" -f $(if ($MarkAsTemplate) {"template"} else {"VM"})) - # Issue Cross VC-vMotion, get Task MoRef back - $task = $vm_view.CloneVM_Task($oDestinationFolder.Id, $strDestinationVMName, $spec) + # Issue Cross vCenter clone, get Task MoRef back + $task = $viewSourceMachine.CloneVM_Task($oDestinationFolder.Id, $strDestinationVMName, $spec) ## return the Task object so that the user can track task as desired Get-Task -Server $strSourceVCenter -Id $task } ## end if From dbf0fdbfc39430bba000820019645bf5c538c996 Mon Sep 17 00:00:00 2001 From: MTBoren Date: Wed, 12 Dec 2018 15:04:32 -0500 Subject: [PATCH 7/7] kill bugs, improve verbosity --- powershell/New-xVM.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powershell/New-xVM.ps1 b/powershell/New-xVM.ps1 index 0e417649..febd82fd 100644 --- a/powershell/New-xVM.ps1 +++ b/powershell/New-xVM.ps1 @@ -92,10 +92,10 @@ function New-xVM { [String[]]$VMNetwork, ## Optional: Name of snapshot on source VM from which to clone the new VM, if any - [String]$SnapshotName, + [parameter(ParameterSetName="FromVM")][String]$SnapshotName, ## Convert the vCenter UUID to uppercase for when performing cross vCenter operation? Some vCenters seemingly require the UUID be in uppercase - [Boolean]$UppercaseUuid, + [Boolean]$UppercaseUuid = $true, ## Switch: Trust all SSL certificates, valid or otherwise? Essentially, "SkipCertificateCheck". Note: this sets this behavior for the whole of the current PowerShell session, not just for this command. [Alias("SkipCertificateCheck")][Switch]$TrustAllCert, @@ -123,7 +123,7 @@ function New-xVM { return true; } } -"@ + "@ } ## end if ## set the CertificatePolicy to essentially skip cert checking by setting the CheckValidationResult() return to always be $true [System.Net.ServicePointManager]::CertificatePolicy = New-Object IDontCarePolicy @@ -159,7 +159,7 @@ function New-xVM { # Dest ResourcePool $oDestinationResourcePool = $VMHost | Get-Cluster | Get-ResourcePool -Name $ResourcePool ## name to use for destination VM - $strDestinationVMName = if ($PSBoundParameters.ContainsKey("DestinationVMName")) {$DestinationVMName} else {$SourceVM.Name} + $strDestinationVMName = if ($PSBoundParameters.ContainsKey("DestinationVMName")) {$DestinationVMName} else {$viewSourceMachine.Name} # Snapshot to clone from, if any if ($PSBoundParameters.ContainsKey("SnapshotName")) {$oSourceSnapshot = $SourceVM | Get-Snapshot -Name $snapshotname} @@ -244,9 +244,9 @@ function New-xVM { if($oSourceSnapshot) {$spec.Snapshot = $oSourceSnapshot.Id} $strShouldProcessMsg_Target = "VMHost '$($VMHost.Name)' in destination vCenter '$($oDestinationVIServer.Name)'" - $strShouldProcessMsg_Action = "Create new {0} '$strDestinationVMName' from source {1} '$($SourceVM.Name)' from vCenter '$strSourceVCenter'" -f $(if ($MarkAsTemplate) {"template"} else {"VM"}), $(if ($bCloneFromTemplate) {"template"} else {"VM"}) + $strShouldProcessMsg_Action = "Create new {0} '$strDestinationVMName' from source {1} '$($viewSourceMachine.Name)' from vCenter '$strSourceVCenter'" -f $(if ($MarkAsTemplate) {"template"} else {"VM"}), $(if ($bCloneFromTemplate) {"template"} else {"VM"}) if ($PSCmdlet.ShouldProcess($strShouldProcessMsg_Target, $strShouldProcessMsg_Action)) { - Write-Verbose -Verbose ("Cloning $($SourceVM.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new {0} named '$strDestinationVMName'" -f $(if ($MarkAsTemplate) {"template"} else {"VM"})) + Write-Verbose -Verbose ("Cloning $($viewSourceMachine.Name) from $strSourceVCenter to $($oDestinationVIServer.Name), creating new {0} named '$strDestinationVMName'" -f $(if ($MarkAsTemplate) {"template"} else {"VM"})) # Issue Cross vCenter clone, get Task MoRef back $task = $viewSourceMachine.CloneVM_Task($oDestinationFolder.Id, $strDestinationVMName, $spec)