vSphere focused Powershell snippets

How many times have you came across a scenario where you know you’ve solved with some Powershell code in the past, but can’t remember which cmdlet, flag or syntax you used?

I’ve done this to myself countless times, so over the last couple of months I’ve made a conscious effort to centrally write down snippets of code that I’ve used, so future me will be grateful for (helping me save time and some sanity also).

Below are some snippets of code that you may find useful in your day to day tasks as a vSphere admin. Some of these examples would’ve been pinched from other blogs and others I’ve crafted myself. I’ve tried to reference the original blog where possible.

Virtual Machine Operations

Removing snapshots:
The csv should contain headers vmname and snapname which I just grab from RVTools. This will go through and delete one snapshot at a time, if you’re a gambler, you can do them all at once by adding -RunAsync but I would not recommend it in production.

$vms = Import-csv .\snaps.csv
$vms | %{Get-Snapshot $_.vmname -name $_.snapname | Remove-snapshot -confirm:$false}

The above code is good if you want to target specific snapshots for VMs, but if you want to remove all snapshots against a VM, a slight modification to the above code, show below will work. The CSV you pass in here should only contain a list of VM names:

foreach ($vm in $vmList) {Get-Snapshot $vm | Remove-Snapshot}

Note: Previously we had a $vm | % {… instead of a foreach statement. % is short for foreach and we are passing in the list of VMs into it.

Deploy OVF with customizatons
If you need to deploy a few instances of the same appliance with some customizations, this is really helpful.

First you need to import the OVF and extract the varaibles that are available for customization

$ovfpath = ".\VendorAppliance.ova"
$ovfconfig = Get-OvfConfiguration -ovf $ovfpath
$ovfconfig.ToHashTable() | FT -autosize

Once you have this, you can pick out which ones you need to customize and declrate the values as below.

$ovfconfig.NetworkMapping.primary.Value = "CLS01_VL3"
$ovfconfig.NetworkMapping.mon0.Value = "CLS01_Monitor"
$datastore = "cls01_fcp"
$esxhost = "esxi01.vkarps.local"
$vmname ="AppServer"
$DRSRulename = "AppServer1tohost1"
$HostRule = "Host1"
$cluster = "CLS01"

Once that it all done, it’s time to deploy the appliance
Import-VApp $ovfpath -OvfConfiguration $ovfconfig -name $vmname -VMHost $esxhost -Datastore $datastore

Prevent tools from re-sizing display

$vmname = "KioskVM"
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$opt = New-Object VMware.Vim.OptionValue
$opt.Key = "guestInfo.svga.wddm.modeset"
$opt.Value = "FALSE"
$spec.ExtraConfig += $opt
$vm = Get-VM -Name $vmName

Move single VM to defined folder

$vmfolder = "SQL"
Move-VM -vm SQL01 -destination $vmfolder

Move multiple VMs to defined folder

$vmFolder = "SQL"
$vms = import-csv vms.csv
Foreach ($vm in $vms){Move-VM -vm $vm -destination $vmfolder}

Move VMs from one datastore to another

$vms = Get-datastore "cls01_vol01" | Get-VM
foreach($vm in $vms){
Move-VM $vm.name -Datastore "cls01_vol02"

Move list of virtual machines to a defined host

$vms = Import-csv .\SQLVMs.csv
foreach($vm in $vms){
Move-VM $vm.name -Destination "esxi01.vkarps.local"


Create new deploy rule with image profile
If you want to create a new AutoDeploy rule, there is no commandlet that I’ve found (as of writing this) that will list out the image profile objects from the repository. The way we get around this is by grabbing the image profile from an existing host that already has the image applied, then passing that object into the New-DeployRule command.

$img = Get-VMHostImageProfile esxi01.vkarps.local
New-DeployRule -Name DeployRuleName -Item $img,Cluster1 -Pattern "ipv4="

Copy and existing Autodeploy rule and set some new patterns
This is useful if you want to use an existing image or host profile and apply it to a new target / pattern. You could also use this to solve the same issue that the previous code did.

Copy-DeployRule -DeployRule "testrule" -ReplaceItem host_profile,targetcluster -ReplacePattern "ipv4=192.XXX.1.10-192.XXX.1.20"

Forcing a compliance update
So, if you’ve ever played with AutoDeploy you will know it has some annoying ‘features’. One is when you’re changing an image profile for a deploy rule, the host won’t automatically pick up that change on the next reboot (in my experience anyway). The below snippet will force the AutoDeploy service to refresh it’s mappings for the target host.

Test-DeployRuleSetCompliance -VMhost esxi01.vkarps.local | Repair-DeployRuleSetCompliance

Host Operations

Create a new VMKernel port
Enabling vMotion with JumboFrames in this example

New-VMHostNetworkAdapter -VMHost esxi01.vkarps.local -PortGroup Cluster1_VMK_10.1.2.0_VL2_vmotion -VirtualSwitch Cluster1_vds -IP -SubnetMask -Mtu 9000 -vmotionenabled $true

Mounting an NFS datastore to a host
New-Datastore -Nfs -VMHost esxi01.vkarps.local -Name cls1_syno_vol01 -Path "cls1_syno_vol01" -NfsHost

Mounting an NFS datastore to all hosts in cluster

$hosts = Get-Cluster "CLS01" | Get-VMHost
foreach($hst in $hosts){
New-Datastore -Nfs -VMHost $hst.name -Name cls1_syno_vol01 -Path "cls1_syno_vol01" -NfsHost

Set dump collector configuration for defined cluster

$cluster = CLS01
Get-Cluster $clsuter | Get-VMHost | Set-VMHostDumpCollector -HostVNic "vmk0" -NetworkServerIP "" -NetworkServerPort 6500

Configure Multipathing policy variants
You may have a scenario that a number of hosts across clusters have a particular lun set to fixed path and you need to set it to round robin. The below snippet will do that for the defined scsi lun

$hosts = Import-csv .\FixedPathHosts.csv
Foreach ($h in $hosts){
Get-VMhost $h.name | Get-ScsiLun naa.6000155000000010b000801ebeaaaa50 | Set-ScsiLun -MultipathPolicy "roundrobin"

Similar to above, but for all hosts in the clusters
Get-Cluster CLS01 | Get-VMhost | Get-ScsiLun naa.6000155000000010b000801ebeaaaa50 | Set-ScsiLun -MultipathPolicy "roundrobin"

For a single ESXi host, but any lun stating with naa.600

Get-VMHost esxi01.vkarps.local | Get-ScsiLun -CanonicalName "naa.600*" | Set-ScsiLun -MultipathPolicy "roundrobin"


We all often get asked to report on various and perhaps obscure things. Here are some that I’ve found I either regularly get asked for or have found handy to extract data quickly.

VM Properties
Sometimes you may need to get some metadata about a list of VMs for folks, below is an example of taking a list of VMs and finding out which hosts they are currently running on and spitting out the result to a file. It can be easily modified to find different VM properties.

$vmList = Get-Content vm.txt
$output = foreach ($vmName in $vmList) {Get-VM $vmName | Select-Object -Property Name,VMHost}
$output | Export-Csv c:\temp\VMHostList.csv

DRS Groups
Say you need to get the specs (or attributes) of virtual machines in a DRS Group. Below is an example of how to grab a list of objects in a DRS group and find the specs we are interested in (vCPU count and Memory allocation).

$a=(get-DRSClusterGroup -cluster Cluster2 -Name "SQL VMS")
$c= foreach ($vm in $b) {get-VM $vm | Select Name, NumCPU,MemoryGB }
$c | export-csv Cluster2_sqlVMs.csv

compare object (DRS group to what’s on the host)
If you have some “License restrictions” in your environment, you may have to force a number of VMs onto a single or gorup of hosts using DRS groups. It’s quite hard to maintain a DRS rule that negates the first rule. I.e: Anything not in group “SQL” group, do not put it onto “SQL Host” host.
I found the below snippet helpful to compare the VM DRS Group to what is actually running on the host. It is much quicker than uing a spreadsheet or going through it manually. The compare-object cmdlet can be used for a number of use cases.

$a = get-cluster CLS01 | Get-DrsClusterGroup -name "SQL Guests" | Select member
$b = Get-VMHost esxi01.vkarps.local | Get-VM | Select name
$a.member.name.count (This line and the one below are not required, just gives you an indication if you need to run the last line or not. I.e if they're the same, you're good; in this scenario).
compare-object $a.member.name $b.name

Get datastore mount path (NFS)

Get-VMHost esxi01.vkarps.local | get-Datastore | Select Name,@{n='RemoteHost';e={$_.RemoteHost[0]}},RemotePath | Export-csv CLS01_NFS_DatastoreDetails.csv -NoTypeInformation

Get datastores / datastore cluster relationship

Get-Datastore -Location DC2 | Select @{N=’DSC’;E={Get-DatastoreCluster -Datastore $_ | Select -ExpandProperty Name}},Name

Check which port groups have netflow enabled on vDS

Get-vDSwitch clos01_vds | Get-vDPortGroup | Select Name,@{Name="NetflowEnabled";Expression={$_.Extensiondata.Config.defaultPortConfig.ipfixEnabled.Value}}

Get dump collector config for cluster

Get-Cluster CLS01| Get-VMHost | Get-VMHostDumpCollector | FT VMHost,Hostvnic,NetworkServerIP,NetworkserverPort,Enabled

List VMs with ISO mounted in Cluster

Get-Cluster CLS01 | Get-VM | Select Name, @{Label="ISO file"; Expression = { ($_ | Get-CDDrive).ISOPath }} | Export-csv -NoTypeInformation CLS01MountedMedia.csv

Get Power on events for defined VM
This can be adjusted to look at other tasks. Great writeup here with some more detail http://www.lucd.info/2009/11/18/events-dear-boy-events-part-2/

Get-VM SQL01 | Get-VIEvent -MaxSamples ([int]::MaxValue) | where { $_.fullFormattedMessage -like "Task: Power on*" }

Get VM DNS name for VMs with particular string in the name

Get-VM | Where {$_.name -like "*SQL*"} | Select name, @{N="DnsName"; E={$_.ExtensionData.Guest.Hostname}} | FT -autosize

Get VM count for each host and export to csv

Get-VMHost | Select @{N=“Cluster“;E={Get-Cluster -VMHost $_}}, Name, @{N=“NumVM“;E={($_ | Get-VM).Count}} | Sort Cluster, Name | Export-Csv -NoTypeInformation c:\HostVMCount.csv`

Get vMotion events for particular Virtual Machines and export to csv
Ensure you have the Get-VMotion script installed on your machine (see http://www.brianbunke.com/blog/2017/01/03/get-vmotion/)

Import-Module Get-vMotion
$vms =@('SQL01','SQL02');
$array = @()
foreach($vm in $vms){
$array += get-VM $vm | Get-vMotion -days 30 | Select Name,srcHost,dstHost,Duration,StartTime,EndTime
$array | Export-csv -NoTypeInformation .\vMotionEvents.csv

Get VMs Sync Time With Host Setting

Get-VM * | Select @{N='VM Name';E={$_.Name}},@{N='GuestOS';E={$_.ExtensionData.Guest.GuestFullName}},@{N='SyncWithHost';E={$_.ExtensionData.Config.tools.SyncTimeWithHost}} | Export-csv .\timeSync.csv -NoTypeInformation

vDS Operations

Creating a new port group on an existing vDS
In this example we are setting the “Allow Promiscuous” flag to the port group.
$vds = Get-VDSwitch "cls01_vds"
$pg = "cls01_monitor"
$vdspg = $vds | New-VDPortgroup -name $pg
$vdspg | Get-VDSecurityPolicy | Set-VDSecurityPolicy -AllowPromiscuous $true

Export vDS config

$vDSConfLocation = "C:\vDSConfigs"
$vDS = "CLS01_vds"
Get-VDSwitch -name $vDS | Export-VDSwitch -Description “Backup of $($_.Name) VDS” -Destination "$vDSConfLocation\$vDS$($_.Name).Zip" -Force

Create new vDS based off existing one in different vCenter
This can be easily adjusted to copy from within the same vCenter

$srcvCenter = "vc01.vkarps.local"
$dstvCenter = "vc02.vkarps.local"
$DCLocation = "DC2"
$vDS = "CLS01_vds"
$srcVDS = Get-vDSwitch $vDS
$srcPG = $srcvDS | Get-VDPortgroup
New-VDSwitch -Server $dstvCenter -name $vds -Location (get-Datacenter -Server $dstvCenter $DCLocation) -LinkDiscoveryProtocol CDP -LinkDiscoveryProtocolOperation Listen -Mtu $srcVDS.mtu -NumUplinkPorts $srcVDS.NumUplinkPorts -Version 6.5.0
Foreach ($pg in $srcPG)
$pgVLAN = $pg.Extensiondata.Config.DefaultPortConfig.Vlan.VlanID
If ($pg.IsUplink -eq "True"){Write-Host "Skipping Uplink PortGroup" -ForegroundColor yellow}
#If it is not the uplink pg, create it
Get-VDSwitch -Server $dstvCenter -name $vDS | New-VDPortgroup -Name $pg.name -NumPorts $pg.numPorts -VLanId $pgVLAN

Cluster Operations

Create VM DRS Group and add a VM

$vmname = "SQL01"
New-DrsClusterGroup -Name "SQL VMs" -cluster $cluster -vm $vmname

Create Host DRS group and add host

$esxhost = "esxi01.vkarps.local"
New-DrsClusterGroup -Name "Host1" -cluster $cluster -VMHost $esxhost

Create new VM/Host rule from previously created groups

$DRSRulename = "SQL Licensing"
$vmGroup = "SQL VMS"
$hostRule = "Host1"
New-DrsVMHostRule -Name $DRSRulename -Cluster $cluster -VMGroup $vmGroup -VMHostGroup $HostRule -Type "MustRunOn"

Create new cluster based off a cluster in different vCenter

$cluster = "CLS01"
$srcCluster = Get-cluster -name $cluster
$DCLocation = "DC2"
$dstvCenter = "vc01.karps.local"
$srcvCenter = "vc02.vkarps.local"
New-Cluster -Server $dstvCenter -name $srcCluster.name -Location $DClocation -DRSEnabled -DRSAutomationLevel $srcCluster.DRSAutomationLevel -EVCMode $srcCluster.EVCMode -HAEnabled

Datastore Operations

Create Datastore Cluster and disable SIOC

New-DatastoreCluster -Name $dsCluster -Location $DCLocation

Set SDRS to manual and disable SIOC on Datastore Cluster

Set-Datastorecluster -sdrsAutomationLevel Manual -IOLoadBalanceEnabled $false

Create folder under datastores view under existing folder

(get-view (get-view -ViewType datacenter -Filter @{"name"="DC2"}).DatastoreFolder).CreateFolder("SQL")

I hope that some of these examples have been useful to you or perhaps given you a better idea on how you can use powershell for particular use cases, where you can then adopt it to your use case.

That’s all I have for now, I’ll eventually put this into github and keep adding to it there. If you have any feedback, please leave a comment below.

vSphere Replication ‘Solution user detail’ certificate is invalid

I came across an odd ball today when upgrading a customers vSphere Replication appliance from 5.8.1 to 6.0. For those who have done a VR in place upgrade before, you will know that there is really not much to it; Mount ISO, Install Update, Reboot, done. When VR 6.0 was introduced an additional step is required after you perform the upgrade, you had to go in and register it with your lookup service. All this requires is the lookup service url and the SSO administrator credentials and you’re done as per the documentation here.

Instead I was presented with this error message:

'Solution user detail' certificate is invalid - certificateException java.security.cert.CertificateExpiredException: NotAfter: Fri Jun 17 23:01:55 UTC 2016


Its probably worth noting that this has come off the back of a vCenter upgrade to 6.5, so ‘solution user’ automatically got me looking at SSO solution users in the web client and the vCenter extension manager to validate that the certificate thumbprints matched up.

Sure enough, the thumbprint that was registered with vCenter matched the one of the vSphere Replication appliance. My google-fu didnt get me any furhter either, next thing that came to mind was ssh and logs. I saw the secuiry tab and figured I would enable ssh from there…nope, no enable ssh option there dummy. What I did see on the secuirty tab was this.

Right, expity date matches the error message!

So here is what you need to do:

Head back over on the configuration page there is a “Install a new SSL Certificate” section where you can generate and install a self signed or your own certificate.

Hit the Generate and Install button, validate the warning that it will overwrite the existing one and let it do its thing.



Once it is done, you will be prompted by the following message.


Reload your brower like it states, even though it seems as if does it when it takes you do the login page.

Once you’re back in, populate your Lookup Service URL and SSO Administrator credentials at the configuration tab and hit Save and Restart service. If all went well, you should get the below message.