Different Tools; Same Result – vSphere Tags

Following the last blog post on create vSphere Port Groups, let’s take a look at creating Tags and Tag Categories.
Let’s first look at the process via the GUI, in this case, the vSphere Client. (Based on vSphere 7.0.3c)
vSphere Client
I wont go into to much detail here as this information is readily available, but here is a brief run through.
After logging into the vSphere Client, select the menu followed by Tags & Custom Attributes.

You the have the option to select either Tags or Categories, followed by the ‘New’ option.

For Categories you need to provide the Category name, optional description, the cardinality (single or multiple) and select the objects that can have this tag associated with it.

Then with Tags, you need to provide the name, optional description and the category the tag will be part of.

Now this may be ok for one or two, but if you need to create in bulk, this will take a while! Lets look as some alternatives.
PowerShell
Firstly, PowerShell, specifically the VMware PowerCLI PowerShell module. Here are examples of the using the cmdlets New-TagCategory and New-Tag to create the same thing we did in the vSphere Client.
#Tag Categories
New-TagCategory -Name "costcentre" -Description "Created with PowerCLI" -Cardinality "MULTIPLE" -EntityType "VirtualMachine", "Datastore"
#Tags
New-Tag -Name "0001" -Category "costcentre" -Description "Created with PowerCLI"
Below is the output from PowerShell after running the script above:
Name Cardinality Description ---- ----------- ----------- costcentre Multiple Created with PowerCLI Name Category Description ---- -------- ----------- 0001 costcentre Created with PowerCLI
Now this isn’t much quicker than doing it in the vSphere Client so here is one way to create in bulk.
Here is a custom array with multiple categories and the additional values needed to create a Category.
$TagCategories = @(
[pscustomobject]@{Name = "costcentre"; Cardinality = "MULTIPLE"; EntityType = "VirtualMachine", "Datastore" }
[pscustomobject]@{Name = "environment"; Cardinality = "SINGLE"; EntityType = "VirtualMachine", "Datastore" }
[pscustomobject]@{Name = "nsx-tier"; Cardinality = "MULTIPLE"; EntityType = "VirtualMachine" }
)
foreach ($Category in $TagCategories) {
New-TagCategory -Name $Category.Name -Cardinality $Category.Cardinality -EntityType $Category.EntityType -Description "Created with PowerCLI"
}
Here is the output:
Name Cardinality Description ---- ----------- ----------- costcentre Multiple Created with PowerCLI environment Single Created with PowerCLI nsx-tier Multiple Created with PowerCLI
And now the same principal but with Tags.
$Tags = @(
[pscustomobject]@{Name = "0001"; Category = "costcentre" }
[pscustomobject]@{Name = "0002"; Category = "costcentre" }
[pscustomobject]@{Name = "0003"; Category = "costcentre" }
[pscustomobject]@{Name = "0004"; Category = "costcentre" }
[pscustomobject]@{Name = "environment"; Category = "environment" }
[pscustomobject]@{Name = "production"; Category = "environment" }
[pscustomobject]@{Name = "pre-production"; Category = "environment" }
[pscustomobject]@{Name = "test"; Category = "environment" }
[pscustomobject]@{Name = "development"; Category = "environment" }
[pscustomobject]@{Name = "web"; Category = "nsx-tier" }
[pscustomobject]@{Name = "app"; Category = "nsx-tier" }
[pscustomobject]@{Name = "data"; Category = "nsx-tier" }
)
foreach ($Tag in $Tags) {
New-Tag -Name $Tag.Name -Category $Tag.Category -Description "Created with PowerCLI"
}
Output:
Name Category Description ---- -------- ----------- 0001 costcentre Created with PowerCLI 0002 costcentre Created with PowerCLI 0003 costcentre Created with PowerCLI 0004 costcentre Created with PowerCLI environment environment Created with PowerCLI production environment Created with PowerCLI pre-production environment Created with PowerCLI test environment Created with PowerCLI development environment Created with PowerCLI web nsx-tier Created with PowerCLI app nsx-tier Created with PowerCLI data nsx-tier Created with PowerCLI

That is just one way to create multiple Categories and Tags. You could take this information from a CSV file using the ‘Get-Content’ cmdlet as an alternative to creating the array manually.
Terraform
Now let’s take a look at using Terraform to achieve the same result. Terraform is an infrastructure and code tool used to manage infrastructure in the form of configuration files and state:
#Providers
provider "vsphere" {
vsphere_server = "vcsa-fqdn"
user = "domain\\user"
password = "password"
allow_unverified_ssl = false
}
#Tag categories
resource "vsphere_tag_category" "costcentre" {
name = "costcentre"
description = "Managed by Terraform"
cardinality = "MULTIPLE"
associable_types = [
"VirtualMachine",
"Datastore",
]
}
resource "vsphere_tag_category" "environment" {
name = "environment"
description = "Managed by Terraform"
cardinality = "SINGLE"
associable_types = [
"VirtualMachine",
"Datastore",
]
}
resource "vsphere_tag_category" "nsx-tier" {
name = "nsx-tier"
description = "Managed by Terraform"
cardinality = "MULTIPLE"
associable_types = [
"VirtualMachine"
]
}
#Tags
#Local values
locals {
costcentre_tags = ["0001", "0002", "0003", "0004"]
environment_tags = ["production", "pre-production", "test", "development"]
nsx_tier_tags = ["web", "app", "data"]
}
#Resources
resource "vsphere_tag" "costcentre-tags" {
for_each = toset(local.costcentre_tags)
name = each.key
category_id = vsphere_tag_category.costcentre.id
description = "Managed by Terraform"
}
resource "vsphere_tag" "environment-tags" {
for_each = toset(local.environment_tags)
name = each.key
category_id = vsphere_tag_category.environment.id
description = "Managed by Terraform"
}
resource "vsphere_tag" "nsx-tier-tags" {
for_each = toset(local.nsx_tier_tags)
name = each.key
category_id = vsphere_tag_category.nsx-tier.id
description = "Managed by Terraform"
}
Lets break this down.
First we are specifying which terraform provider we want to use, this will be the vSphere provider in this case. We are then providing some parameters for the provider to connect to your vCenter instance; VCSA FQDN and credentials. You would want make use of variables for this data, but for this blog I am keeping it simple.
provider "vsphere" {
vsphere_server = "vcsa-fqdn"
user = "domain\\user"
password = "password"
allow_unverified_ssl = false
}
We then have three vsphere_tag_category resource blocks, one for each of the categories we want to create. This again provides values for cardinality and associable types like we did in PowerShell.
resource "vsphere_tag_category" "costcentre" {
name = "costcentre"
description = "Managed by Terraform"
cardinality = "MULTIPLE"
associable_types = [
"VirtualMachine",
"Datastore",
]
}
resource "vsphere_tag_category" "environment" {
name = "environment"
description = "Managed by Terraform"
cardinality = "SINGLE"
associable_types = [
"VirtualMachine",
"Datastore",
]
}
resource "vsphere_tag_category" "nsx-tier" {
name = "nsx-tier"
description = "Managed by Terraform"
cardinality = "MULTIPLE"
associable_types = [
"VirtualMachine"
]
}
Next we are going to create the tags, but I am going to use a set of local variables to then pass into the three vsphere_tag resource blocks to reduce the amount of repeating code.
Here are the local variables. This is similar to creating the array we did in PowerShell.
locals {
costcentre_tags = ["0001", "0002", "0003", "0004"]
environment_tags = ["production", "pre-production", "test", "development"]
nsx_tier_tags = ["web", "app", "data"]
}
And then the resource blocks, notice the for_each parameter. For each Tag Category, it will cycle through each value in the locals array for each category. This is just like we did in PowerShell foreach function earlier.
resource "vsphere_tag" "costcentre-tags" {
for_each = toset(local.costcentre_tags)
name = each.key
category_id = vsphere_tag_category.costcentre.id
description = "Managed by Terraform"
}
resource "vsphere_tag" "environment-tags" {
for_each = toset(local.environment_tags)
name = each.key
category_id = vsphere_tag_category.environment.id
description = "Managed by Terraform"
}
resource "vsphere_tag" "nsx-tier-tags" {
for_each = toset(local.nsx_tier_tags)
name = each.key
category_id = vsphere_tag_category.nsx-tier.id
description = "Managed by Terraform"
}
Now when we run ‘terraform apply’ from the command line to apply for code, this is the output:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # vsphere_tag.costcentre-tags["0001"] will be created + resource "vsphere_tag" "costcentre-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "0001" } # vsphere_tag.costcentre-tags["0002"] will be created + resource "vsphere_tag" "costcentre-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "0002" } # vsphere_tag.costcentre-tags["0003"] will be created + resource "vsphere_tag" "costcentre-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "0003" } # vsphere_tag.costcentre-tags["0004"] will be created + resource "vsphere_tag" "costcentre-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "0004" } # vsphere_tag.environment-tags["development"] will be created + resource "vsphere_tag" "environment-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "development" } # vsphere_tag.environment-tags["pre-production"] will be created + resource "vsphere_tag" "environment-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "pre-production" } # vsphere_tag.environment-tags["production"] will be created + resource "vsphere_tag" "environment-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "production" } # vsphere_tag.environment-tags["test"] will be created + resource "vsphere_tag" "environment-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "test" } # vsphere_tag.nsx-tier-tags["app"] will be created + resource "vsphere_tag" "nsx-tier-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "app" } # vsphere_tag.nsx-tier-tags["data"] will be created + resource "vsphere_tag" "nsx-tier-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "data" } # vsphere_tag.nsx-tier-tags["web"] will be created + resource "vsphere_tag" "nsx-tier-tags" { + category_id = (known after apply) + description = "Managed by Terraform" + id = (known after apply) + name = "web" } # vsphere_tag_category.costcentre will be created + resource "vsphere_tag_category" "costcentre" { + associable_types = [ + "Datastore", + "VirtualMachine", ] + cardinality = "MULTIPLE" + description = "Managed by Terraform" + id = (known after apply) + name = "costcentre" } # vsphere_tag_category.environment will be created + resource "vsphere_tag_category" "environment" { + associable_types = [ + "Datastore", + "VirtualMachine", ] vsphere_tag.environment-tags["production"]: Creating... vsphere_tag.environment-tags["pre-production"]: Creating... vsphere_tag_category.nsx-tier: Creation complete after 0s [id=urn:vmomi:InventoryServiceCategory:20a2167a-b0f8-4a60-9d29-6c7ca57711ef:GLOBAL] vsphere_tag.nsx-tier-tags["data"]: Creating... vsphere_tag.nsx-tier-tags["app"]: Creating... vsphere_tag.nsx-tier-tags["web"]: Creating... vsphere_tag_category.costcentre: Creation complete after 0s [id=urn:vmomi:InventoryServiceCategory:28a909f5-ee41-4d94-b228-b5e96e09284e:GLOBAL] vsphere_tag.costcentre-tags["0004"]: Creating... vsphere_tag.costcentre-tags["0002"]: Creating... vsphere_tag.costcentre-tags["0003"]: Creating... vsphere_tag.environment-tags["development"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:5b63e350-ef6e-4bbc-a633-09c9047b327b:GLOBAL] vsphere_tag.costcentre-tags["0001"]: Creating... vsphere_tag.environment-tags["pre-production"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:e2a8737c-e42a-4c6f-b9a8-716a1681d0c0:GLOBAL] vsphere_tag.nsx-tier-tags["data"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:b9d3394d-388c-4018-b7b2-9e4d3da8287b:GLOBAL] vsphere_tag.costcentre-tags["0002"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:8a482528-5d67-40e9-86cb-4dbf566f85ac:GLOBAL] vsphere_tag.nsx-tier-tags["web"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:5a325904-4dfd-46ac-b0db-37fd6fda1533:GLOBAL] vsphere_tag.environment-tags["production"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:89c609b9-7f90-457d-9f71-0bd0b7cc667d:GLOBAL] vsphere_tag.nsx-tier-tags["app"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:45c2dd0e-533a-4917-82be-987d3245137a:GLOBAL] vsphere_tag.costcentre-tags["0004"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:230db56e-7352-4e14-ba63-0ad4b4c0ba18:GLOBAL] vsphere_tag.environment-tags["test"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:ebcf1809-8cae-4cb2-a5fa-82a492e54227:GLOBAL] vsphere_tag.costcentre-tags["0001"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:e4649ad2-08d2-4dcd-aabf-4e2d74f93a36:GLOBAL] vsphere_tag.costcentre-tags["0003"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:18de9eca-456c-4539-ad6c-19d625ac5be7:GLOBAL] Apply complete! Resources: 14 added, 0 changed, 0 destroyed.

For more information on the vSphere provider from Terraform, check out this link.
I hope this has given you some idea’s on how you can perhaps leverage other options beside the GUI, especially when looking to build or configure in bulk. All the code in this blog can be found on my GitHub here.
Thanks for reading!