Different Tools; Same Result – vSphere Tags

Posted by Stephan McTighe on 15 Feb 2022

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.

1#Tag Categories
2New-TagCategory -Name "costcentre" -Description "Created with PowerCLI" -Cardinality "MULTIPLE" -EntityType "VirtualMachine", "Datastore"
3#Tags
4New-Tag -Name "0001" -Category "costcentre" -Description "Created with PowerCLI"

Below is the output from PowerShell after running the script above:

1Name        Cardinality     Description
2----        -----------     -----------
3costcentre  Multiple        Created with PowerCLI
4
5Name        Category        Description
6----        --------        -----------
70001        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.

1$TagCategories = @(
2    [pscustomobject]@{Name = "costcentre"; Cardinality = "MULTIPLE"; EntityType = "VirtualMachine", "Datastore" }
3    [pscustomobject]@{Name = "environment"; Cardinality = "SINGLE"; EntityType = "VirtualMachine", "Datastore" }
4    [pscustomobject]@{Name = "nsx-tier"; Cardinality = "MULTIPLE"; EntityType = "VirtualMachine" }
5)
6foreach ($Category in $TagCategories) {
7    New-TagCategory -Name $Category.Name -Cardinality $Category.Cardinality -EntityType $Category.EntityType -Description "Created with PowerCLI"
8}

Here is the output:

1Name         Cardinality    Description
2----         -----------    -----------
3costcentre   Multiple       Created with PowerCLI
4environment  Single         Created with PowerCLI
5nsx-tier     Multiple       Created with PowerCLI

And now the same principal but with Tags.

 1$Tags = @(
 2    [pscustomobject]@{Name = "0001"; Category = "costcentre" }
 3    [pscustomobject]@{Name = "0002"; Category = "costcentre" }
 4    [pscustomobject]@{Name = "0003"; Category = "costcentre" }
 5    [pscustomobject]@{Name = "0004"; Category = "costcentre" }
 6    [pscustomobject]@{Name = "environment"; Category = "environment" }
 7    [pscustomobject]@{Name = "production"; Category = "environment" }
 8    [pscustomobject]@{Name = "pre-production"; Category = "environment" }
 9    [pscustomobject]@{Name = "test"; Category = "environment" }
10    [pscustomobject]@{Name = "development"; Category = "environment" }
11    [pscustomobject]@{Name = "web"; Category = "nsx-tier" }
12    [pscustomobject]@{Name = "app"; Category = "nsx-tier" }
13    [pscustomobject]@{Name = "data"; Category = "nsx-tier" }
14)
15foreach ($Tag in $Tags) {
16    New-Tag -Name $Tag.Name -Category $Tag.Category -Description "Created with PowerCLI"
17}

Output:

 1Name                           Category                       Description
 2----                           --------                       -----------
 30001                           costcentre                     Created with PowerCLI
 40002                           costcentre                     Created with PowerCLI
 50003                           costcentre                     Created with PowerCLI
 60004                           costcentre                     Created with PowerCLI
 7environment                    environment                    Created with PowerCLI
 8production                     environment                    Created with PowerCLI
 9pre-production                 environment                    Created with PowerCLI
10test                           environment                    Created with PowerCLI
11development                    environment                    Created with PowerCLI
12web                            nsx-tier                       Created with PowerCLI
13app                            nsx-tier                       Created with PowerCLI
14data                           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:

 1#Providers
 2provider "vsphere" {
 3  vsphere_server       = "vcsa-fqdn"
 4  user                 = "domain\\user"
 5  password             = "password"
 6  allow_unverified_ssl = false
 7}
 8#Tag categories
 9resource "vsphere_tag_category" "costcentre" {
10  name        = "costcentre"
11  description = "Managed by Terraform"
12  cardinality = "MULTIPLE"
13  associable_types = [
14    "VirtualMachine",
15    "Datastore",
16  ]
17}
18resource "vsphere_tag_category" "environment" {
19  name        = "environment"
20  description = "Managed by Terraform"
21  cardinality = "SINGLE"
22  associable_types = [
23    "VirtualMachine",
24    "Datastore",
25  ]
26}
27resource "vsphere_tag_category" "nsx-tier" {
28  name        = "nsx-tier"
29  description = "Managed by Terraform"
30  cardinality = "MULTIPLE"
31  associable_types = [
32    "VirtualMachine"
33  ]
34}
35#Tags
36#Local values
37locals {
38  costcentre_tags  = ["0001", "0002", "0003", "0004"]
39  environment_tags = ["production", "pre-production", "test", "development"]
40  nsx_tier_tags    = ["web", "app", "data"]
41}
42#Resources
43resource "vsphere_tag" "costcentre-tags" {
44  for_each    = toset(local.costcentre_tags)
45  name        = each.key
46  category_id = vsphere_tag_category.costcentre.id
47  description = "Managed by Terraform"
48}
49resource "vsphere_tag" "environment-tags" {
50  for_each    = toset(local.environment_tags)
51  name        = each.key
52  category_id = vsphere_tag_category.environment.id
53  description = "Managed by Terraform"
54}
55resource "vsphere_tag" "nsx-tier-tags" {
56  for_each    = toset(local.nsx_tier_tags)
57  name        = each.key
58  category_id = vsphere_tag_category.nsx-tier.id
59  description = "Managed by Terraform"
60}

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.

1provider "vsphere" {
2  vsphere_server       = "vcsa-fqdn"
3  user                 = "domain\\user"
4  password             = "password"
5  allow_unverified_ssl = false
6}

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.

 1resource "vsphere_tag_category" "costcentre" {
 2  name        = "costcentre"
 3  description = "Managed by Terraform"
 4  cardinality = "MULTIPLE"
 5  associable_types = [
 6    "VirtualMachine",
 7    "Datastore",
 8  ]
 9}
10resource "vsphere_tag_category" "environment" {
11  name        = "environment"
12  description = "Managed by Terraform"
13  cardinality = "SINGLE"
14  associable_types = [
15    "VirtualMachine",
16    "Datastore",
17  ]
18}
19resource "vsphere_tag_category" "nsx-tier" {
20  name        = "nsx-tier"
21  description = "Managed by Terraform"
22  cardinality = "MULTIPLE"
23  associable_types = [
24    "VirtualMachine"
25  ]
26}

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.

1locals {
2  costcentre_tags  = ["0001", "0002", "0003", "0004"]
3  environment_tags = ["production", "pre-production", "test", "development"]
4  nsx_tier_tags    = ["web", "app", "data"]
5}

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.

 1resource "vsphere_tag" "costcentre-tags" {
 2  for_each    = toset(local.costcentre_tags)
 3  name        = each.key
 4  category_id = vsphere_tag_category.costcentre.id
 5  description = "Managed by Terraform"
 6}
 7resource "vsphere_tag" "environment-tags" {
 8  for_each    = toset(local.environment_tags)
 9  name        = each.key
10  category_id = vsphere_tag_category.environment.id
11  description = "Managed by Terraform"
12}
13resource "vsphere_tag" "nsx-tier-tags" {
14  for_each    = toset(local.nsx_tier_tags)
15  name        = each.key
16  category_id = vsphere_tag_category.nsx-tier.id
17  description = "Managed by Terraform"
18}

Now when we run ’terraform apply’ from the command line to apply for code, this is the output:

  1Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  2  + create
  3
  4Terraform will perform the following actions:
  5
  6  # vsphere_tag.costcentre-tags["0001"] will be created
  7  + resource "vsphere_tag" "costcentre-tags" {
  8      + category_id = (known after apply)
  9      + description = "Managed by Terraform"
 10      + id          = (known after apply)
 11      + name        = "0001"
 12    }
 13
 14  # vsphere_tag.costcentre-tags["0002"] will be created
 15  + resource "vsphere_tag" "costcentre-tags" {
 16      + category_id = (known after apply)
 17      + description = "Managed by Terraform"
 18      + id          = (known after apply)
 19      + name        = "0002"
 20    }
 21
 22  # vsphere_tag.costcentre-tags["0003"] will be created
 23  + resource "vsphere_tag" "costcentre-tags" {
 24      + category_id = (known after apply)
 25      + description = "Managed by Terraform"
 26      + id          = (known after apply)
 27      + name        = "0003"
 28    }
 29
 30  # vsphere_tag.costcentre-tags["0004"] will be created
 31  + resource "vsphere_tag" "costcentre-tags" {
 32      + category_id = (known after apply)
 33      + description = "Managed by Terraform"
 34      + id          = (known after apply)
 35      + name        = "0004"
 36    }
 37
 38  # vsphere_tag.environment-tags["development"] will be created
 39  + resource "vsphere_tag" "environment-tags" {
 40      + category_id = (known after apply)
 41      + description = "Managed by Terraform"
 42      + id          = (known after apply)
 43      + name        = "development"
 44    }
 45
 46  # vsphere_tag.environment-tags["pre-production"] will be created
 47  + resource "vsphere_tag" "environment-tags" {
 48      + category_id = (known after apply)
 49      + description = "Managed by Terraform"
 50      + id          = (known after apply)
 51      + name        = "pre-production"
 52    }
 53
 54  # vsphere_tag.environment-tags["production"] will be created
 55  + resource "vsphere_tag" "environment-tags" {
 56      + category_id = (known after apply)
 57      + description = "Managed by Terraform"
 58      + id          = (known after apply)
 59      + name        = "production"
 60    }
 61
 62  # vsphere_tag.environment-tags["test"] will be created
 63  + resource "vsphere_tag" "environment-tags" {
 64      + category_id = (known after apply)
 65      + description = "Managed by Terraform"
 66      + id          = (known after apply)
 67      + name        = "test"
 68    }
 69
 70  # vsphere_tag.nsx-tier-tags["app"] will be created
 71  + resource "vsphere_tag" "nsx-tier-tags" {
 72      + category_id = (known after apply)
 73      + description = "Managed by Terraform"
 74      + id          = (known after apply)
 75      + name        = "app"
 76    }
 77
 78  # vsphere_tag.nsx-tier-tags["data"] will be created
 79  + resource "vsphere_tag" "nsx-tier-tags" {
 80      + category_id = (known after apply)
 81      + description = "Managed by Terraform"
 82      + id          = (known after apply)
 83      + name        = "data"
 84    }
 85
 86  # vsphere_tag.nsx-tier-tags["web"] will be created
 87  + resource "vsphere_tag" "nsx-tier-tags" {
 88      + category_id = (known after apply)
 89      + description = "Managed by Terraform"
 90      + id          = (known after apply)
 91      + name        = "web"
 92    }
 93
 94  # vsphere_tag_category.costcentre will be created
 95  + resource "vsphere_tag_category" "costcentre" {
 96      + associable_types = [
 97          + "Datastore",
 98          + "VirtualMachine",
 99        ]
100      + cardinality      = "MULTIPLE"
101      + description      = "Managed by Terraform"
102      + id               = (known after apply)
103      + name             = "costcentre"
104    }
105
106  # vsphere_tag_category.environment will be created
107  + resource "vsphere_tag_category" "environment" {
108      + associable_types = [
109          + "Datastore",
110          + "VirtualMachine",
111        ]
112vsphere_tag.environment-tags["production"]: Creating...
113vsphere_tag.environment-tags["pre-production"]: Creating...
114vsphere_tag_category.nsx-tier: Creation complete after 0s [id=urn:vmomi:InventoryServiceCategory:20a2167a-b0f8-4a60-9d29-6c7ca57711ef:GLOBAL]
115vsphere_tag.nsx-tier-tags["data"]: Creating...
116vsphere_tag.nsx-tier-tags["app"]: Creating...
117vsphere_tag.nsx-tier-tags["web"]: Creating...
118vsphere_tag_category.costcentre: Creation complete after 0s [id=urn:vmomi:InventoryServiceCategory:28a909f5-ee41-4d94-b228-b5e96e09284e:GLOBAL]
119vsphere_tag.costcentre-tags["0004"]: Creating...
120vsphere_tag.costcentre-tags["0002"]: Creating...
121vsphere_tag.costcentre-tags["0003"]: Creating...
122vsphere_tag.environment-tags["development"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:5b63e350-ef6e-4bbc-a633-09c9047b327b:GLOBAL]
123vsphere_tag.costcentre-tags["0001"]: Creating...
124vsphere_tag.environment-tags["pre-production"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:e2a8737c-e42a-4c6f-b9a8-716a1681d0c0:GLOBAL]
125vsphere_tag.nsx-tier-tags["data"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:b9d3394d-388c-4018-b7b2-9e4d3da8287b:GLOBAL]
126vsphere_tag.costcentre-tags["0002"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:8a482528-5d67-40e9-86cb-4dbf566f85ac:GLOBAL]
127vsphere_tag.nsx-tier-tags["web"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:5a325904-4dfd-46ac-b0db-37fd6fda1533:GLOBAL]
128vsphere_tag.environment-tags["production"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:89c609b9-7f90-457d-9f71-0bd0b7cc667d:GLOBAL]
129vsphere_tag.nsx-tier-tags["app"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:45c2dd0e-533a-4917-82be-987d3245137a:GLOBAL]
130vsphere_tag.costcentre-tags["0004"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:230db56e-7352-4e14-ba63-0ad4b4c0ba18:GLOBAL]
131vsphere_tag.environment-tags["test"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:ebcf1809-8cae-4cb2-a5fa-82a492e54227:GLOBAL]
132vsphere_tag.costcentre-tags["0001"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:e4649ad2-08d2-4dcd-aabf-4e2d74f93a36:GLOBAL]
133vsphere_tag.costcentre-tags["0003"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:18de9eca-456c-4539-ad6c-19d625ac5be7:GLOBAL]
134
135Apply 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!

If you like my content, consider following me on Twitter so you don’t miss out!

Follow @vStephanMcTighe