I’ve spent enough hours working with VMware support, API folks, and Tagging team where I just need to share this for everyone. The more voices going to VMware about Tagging and vSphere/vRops performance, the faster a solution will be created.

The following Script is extremely rough. You can Edit the connection strings to be more secure with a $cred = get-credential etc. I made it extremely simple so each step can be validated within logs for performance testing.

The Script will :
1 – Connect to vcenter using VIServer and CISServer methods.
2 – Create ten Tag Categories
3 – Create 500 Random String tags within those tag categories
4 – Create a very simply VM template
5 – Clone that template across your test hosts.
6 – Finally, randomly assign a single tag from each category to every VM.

#######Connect to vCenter
connect-viserver “vcenter” -username “administrator@vsphere.local” -pass “**”
connect-cisserver “vcenter” -username “administrator@vsphere.local” -pass “**”
####### Create some tag categories:
new-TagCategory -name “cat1” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat2” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat3” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat4” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat5” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat6” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat7” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat8” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat9” -entityType “VMHost”,”VirtualMachine” -confirm:$false
new-TagCategory -name “cat10” -entityType “VMHost”,”VirtualMachine” -confirm:$false
####### Create 500 tags per category — total of 5,000 Tags
$allCateMethod = Get-CisService com.vmware.cis.tagging.category
$allcate = $allcateMethod.list()
$cates = @()
foreach ($cate in $allcate) {
$cates += $allCateMethod.Get($cate)
}
$cates = $cates | where {$_.name -match “cat”}

$alltagMethod = Get-CisService com.vmware.cis.tagging.tag
foreach($a in $cates){
$x = 0

while($x -lt 500){
$spec = $alltagMethod.Help.create.create_spec
$spec.name = (-join ((65..90) + (97..122) | Get-Random -Count 30 | % {[char]$_}))
$spec.description = “”
$spec.category_id = $a.id.value
$alltagMethod.Create($spec)
$x++
}
}
#######Create your VM Template: – Basic VM then delete the harddisk to reduce need for Datastore space.
$ahost = get-vmhost “vmhost1”
$adatastore = $ahost | get-datastore | sort freespaceGB -desc | select -f 1

new-vm -name “zTagTesterTemplate” -vmhost $ahost -datastore $adatastore -memoryGB “0.004” -confirm:$false -runasync
$avm = get-vm “zTagTesterTemplate”
$avm | get-harddisk| remove-harddisk -DeletePermanently -confirm:$false
$avm | set-vm -totemplate -confirm:$false
$thetemplate = get-template -name “zTagTesterTemplate”

#######Create a bunch of VMs… Thread this to as many hosts as you want… Going Serial here because I dont know your lab…
####### uncomment the new-vm command below to select the best for your lab…
####### I suggest using four to six ESXi hosts because of vSphere config limitation of VMs per host…
####### edit the MaxCount to match your host count… four hosts – 4,000 VMs… six hosts, 6,000 VMs…
####### 1000 is configured below if you are testing with one host….
####### I tested with 5,600 VMs to exaggerate the impact/results within vRops. “Bigger impact on the charts”
####### Only used if you want to create more than 1000 VMs and want to thread them across more hosts.
#######$hostlist = get-cluster | get-vmhost
####### Vars taken from above… edit if you want to change it here….
#######$ahost = get-vmhost “vmhost1”
#######$adatastore = $ahost | get-datastore | sort freespaceGB -desc | select -f 1
#######$thetemplate = get-template -name “zTagTesterTemplate”

$count = 0
$maxcount = 1000
$vmnamePrefix = “zTagTester”
while ($count -lt $maxcount){
$vmname = $vmnamePrefix + $count
#######New-vm -Name $vmname -VMhost ($hostlist | get-random) -Template $thetemplate -Datastore (get-datastore | sort freespaceGB -desc | select -f 1) -runAsync
New-vm -Name $vmname -VMhost $ahost -Template $thetemplate -Datastore $adatastore -runAsync
$count = $count + 1
}

#######Assign Tags to your VMs… I suggest looking at vRops at this time and watch as these commands run.
####### I’m populating the tag vars first because the tagging API could stop responding later for a slower get and set call.

$tag1 = get-tagcategory | where {$_.name -eq “cat1”} | get-tag
$tag2 = get-tagcategory | where {$_.name -eq “cat2”} | get-tag
$tag3 = get-tagcategory | where {$_.name -eq “cat3”} | get-tag
$tag4 = get-tagcategory | where {$_.name -eq “cat4”} | get-tag
$tag5 = get-tagcategory | where {$_.name -eq “cat5”} | get-tag
$tag6 = get-tagcategory | where {$_.name -eq “cat6”} | get-tag
$tag7 = get-tagcategory | where {$_.name -eq “cat7”} | get-tag
$tag8 = get-tagcategory | where {$_.name -eq “cat8”} | get-tag
$tag9 = get-tagcategory | where {$_.name -eq “cat9”} | get-tag
$tag10 = get-tagcategory | where {$_.name -eq “cat10”} | get-tag

$allvms = get-vm | where {$_.name -match “ztagtester”}

####### using counter to show where you are in the assignment.
####### I Thread this into different powershell windows because the API is a bit slow using New-tagassignment. (could try cisserver assignment methods in your lab)
$count = 0
foreach($avm in $allvms){
$avm | new-tagassignment -tag ($tag1|get-random)
$avm | new-tagassignment -tag ($tag2|get-random)
$avm | new-tagassignment -tag ($tag3|get-random)
$avm | new-tagassignment -tag ($tag4|get-random)
$avm | new-tagassignment -tag ($tag5|get-random)
$avm | new-tagassignment -tag ($tag6|get-random)
$avm | new-tagassignment -tag ($tag7|get-random)
$avm | new-tagassignment -tag ($tag8|get-random)
$avm | new-tagassignment -tag ($tag9|get-random)
$avm | new-tagassignment -tag ($tag10|get-random)
$count++
$count
}

####### Would be nice if new-tagassignment accepted an array of tags…
####### get a single VM and check if it has ten tags assigned….
get-vm ($allvms | select -f 1) | get-tagassignment

#######END – Check vrops collection cycle.

For vSphere tagging, most PowerCLI users go for the “Get-Tag”,”Get-TagCategory”, or “Get-TagAssignment” methods.
Depending on the size of your vCenter, inventory count, but more importantly, Tag count, the CisService methods may provide better performance for vSphere Tagging.

To access these APIs, you will need to log in to vcenter with the following string.  (Adjust vcenter/user/pass as needed)
connect-cisserver “vcenter.pcli.me” -username “administrator@vsphere.local” -pass “mypassword”

To start, each method has documentation.  I will say, it’s not extremely easy to read, but its better than leaving us stranded.
I will show you how to pull the command documentation for Tag Categories and you should be able to translate this for other methods.
Pasting the following two lines will show you the “Operations” you can complete within the “tagging.category.”
$allCateMethod = Get-CisService com.vmware.cis.tagging.category
$allCateMethod.Help

From here, you can expand the help.  Lets say you want to create a new Tag Category.  You add on “create” to the help command and it will show you what you need to complete the command.
$allCateMethod.Help.create
$allCateMethod.Help.create.create_spec

Below, I will post the most common methods I use.

# Create a new Tagging Category: (I included all possible options for the specs after the comment hash)
$allCateMethod = Get-CisService com.vmware.cis.tagging.category
$spec = $allCateMethod.Help.create.create_spec
$spec.name = “myFirstCategory”
$spec.description = “”
$spec.cardinality = “MULTIPLE” # “SINGLE” # yes it needs to be all caps….
$spec.associable_types = “virtualmachine”,”vmhost” # Cluster, Datacenter, Datastore, DatastoreCluster, DistributedPortGroup, DistributedSwitch, Folder, ResourcePool, VApp, VirtualPortGroup
$allCateMethod.Create($spec)

# Get all Tag Categories:
$allCateMethod = Get-CisService com.vmware.cis.tagging.category
$allcate = $allcateMethod.list()
$cates = @()
foreach ($cate in $allcate) {
$cates += $allCateMethod.Get($cate)
}
$cates

# Create a new Tag:  (a Tag needs a category so be sure to create one first)
$alltagMethod = Get-CisService com.vmware.cis.tagging.tag
$spec = $alltagMethod.Help.create.create_spec
$spec.name = “myFirstTag”
$spec.description = “”
$spec.category_id = $cates | where {$_.name -eq “myFirstCategory”} | %{$_.id.value}
$alltagMethod.Create($spec)

# Get all Tags:
$alltagMethod = Get-CisService com.vmware.cis.tagging.tag
$alltag = $alltagMethod.list()
$tags = @()
foreach ($tag in $alltag) {
$tags += $alltagMethod.Get($tag)
}
$tags

# From here, you can now Attach, Detach, or Query tags to objects.   To access those objects (like a Virtual Machine) you will need to connect to vcenter with our VIServer connection.
connect-viserver “vcenter.pcli.me” -username “administrator@vsphere.local” -pass “mypassword”

Now we can pull in object IDs and match them with Tag IDs.
# Attach a Tag to a VirtualMachine:
$mytag = $tags | where {$_.name -eq “myFirstTag”}
$vm = Get-VM -Name “myFirstVM”
$tagAssign = Get-CisService -Name com.vmware.cis.tagging.tag_association
$spec = $tagAssign.Help.attach.object_id.Create()
$spec.type = $vm.ExtensionData.MoRef.Type
$spec.id = $vm.ExtensionData.MoRef.Value
$tagAssign.attach($mytag.id.Value, $spec)

# Then to check the Association:
$tagAsso = Get-CisService -Name com.vmware.cis.tagging.tag_association
$tagAsso.list_attached_objects($mytag.id.value)

If you dig deep into the documentation of these methods, you will find ways to Add, Delete, Create, and even Batch assign tags.  I gave you the starting point here.

One extra for the road:
# Quick method to delete a Tag Category:   (Quick note…. Deleting a category WILL delete all of the tags under it and the API call will not prompt you about it.)
$mycateID = $cates |where {$_.name -eq “myFirstCategory”} |%{$_.id.value}
$allCateMethod = Get-CisService com.vmware.cis.tagging.category
$allcateMethod.Delete($mycateID)

Let me know if you have any questions @vmnick0 on twitter.

Here is a quick line of code you can throw in to your existing scripts if you want to do some validation based on the type of vcenter.
As more companies are moving to the vCenter appliance, MS Windows issues go away.   Until that time, it’s good to alert on disk space and CPU usage for the Windows vCenter.

The quick one-liner to check the vCenter OS:
if(($global:DefaultVIServer | %{$_.extensiondata.content.about.ostype}) -match “win”){ $windows}else{$Linux}

Here is a Windows vCenter “CPU and Disk space” check script:

$vcenter = “vcenter.pcli.me”
$localWindowsAccountUser = “user001”
$localWindowsAccountPass = “test”
## if you want to use a domain account then edit the “$pcstring” var below

Connect-VIServer $vcenter

if(($global:DefaultVIServer | %{$_.extensiondata.content.about.ostype}) -match “win”){
$report=@()
$pcstring = $vcenter+”\”+$localWindowsAccountUser
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $pcstring,(ConvertTo-SecureString -String $localWindowsAccountPass -AsPlainText -Force)
$temp = get-WmiObject win32_logicaldisk -Computername $vcenter -credential $Credential
foreach($b in ($temp | where {$_.drivetype -eq “3”})){
$reportrow = “” | select VMname, DriveLetter, VolumeName,CapacityGB, FreespaceGB,percentFree
$reportrow.vmname = $vcenter
$reportrow.driveletter = $b.DeviceID
$reportrow.VolumeName = $b.VolumeName
$reportrow.capacityGB = “{0:N3}” -f ($b.Size / 1073741824)
$reportrow.freespaceGB = “{0:N3}” -f ($b.FreeSpace / 1073741824)
$reportrow.percentfree = [System.Math]::floor(($b.FreeSpace / $b.Size)*100)
$report += $reportrow
}
$cpucheck = get-WmiObject win32_processor  -Computername $vcenter -credential $Credential | Measure-Object -property LoadPercentage -Average | Select Average
if(([int]($cpucheck.average)) -ge 90){
$smtpServer = “smtp.pcli.me”
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = “Script@pcli.me”
$msg.To.Add(“Team@pcli.me”)
$sub = “vCenter HIGH CPU usage found for “+$vcenter
$msg.subject = $sub
$msg.Body = “”
$smtp.Send($msg)
}}
Disconnect-VIServer -Server $global:DefaultVIServers -Force -confirm:$False

#The following is a good starting template if you have a software suite that uses web based rest APIs.
#** Note,  using Powershell version 4 is highly recommended if the API requires HTTPS and/or uses Self Signed Certs.
# Be sure to edit the user,password and URL below.

# This single line helps get around Cert issues that can prevent you from connecting to the server.  Only use this line if you have connectivity or SSL issues.
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

#The Vars and code:
$User = “testUsername”
$PWord = ConvertTo-SecureString –String “testPassword” –AsPlainText -Force
$Cred = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $User, $PWord

$wc = New-Object System.Net.WebClient
$wc.Headers.Add(“Authorization”, “Basic $( [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(“$($Cred.UserName.TrimStart(‘\’)):$($Cred.GetNetworkCredential().Password)”)) )”)
$temp = $wc.DownloadString(“https://TheApiServer.com/api/”)
$temp

#From here you can use the $temp var to dig further into the API and then format what output you want.   You can review one of my older posts if you need help creating a report for easy exporting. If the API posts back something usable you can just use one of the many powershell Export- functions.