Tag Archives: Tags

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!

vCenter Tag Administration Permissions

The use of VMware tags recently became a requirement for some of my colleagues in an environment that was inherited.  They were faced with being unable to create Tags & Tag Categories or Assign and Delete them, despite ‘having admin rights’.

Upon investigation it became apparent that while the admins had been granted the Administrators Role at the vCenter Object Level, they had had not been granted sufficient rights at the Global Permissions Level, or any rights for that matter.

The following graphic shows the vSphere Inventory hierarchy.

Link to VMware’s article – Here

As you can see from the graphic, assigning privileges at the Global Level is required to manage Tags and additionally, Content Libraries.

You can see below a vSphere Admin, who has the Administrator Role assigned at the vCenter Level, is not able to select the New, Edit, Delete or Add Permission options for Tags or Tag Categories.

In this scenario there were two different permission requirements, one for a vSphere Admin team, the other for a Storage team both of which could be addressed by two existing roles; Administrators and Tagging Admin.  You could of course create a custom role should you have a requirement to do so. Here are the privileges assigned to the tagging admin role for reference.

The vSphere Admin Team required the Administrator Role at the Global Level (root object) so they could manage Content Libraries and Tags, while the Storage Team required the ability to Create and Assign Tags only.

You can assign permissions to a user or a group from multiple Identity Sources, in this scenario, an Active Directory source.  You will need to do this from an account that already has the Administrator Role at the Global Level.  By default, the administrator@vsphere.local account has this privilege. (replace the domain as needed if you have used a different SSO domain name)

From the Menu, select ‘Administration’ and select the ‘Global Permissions’ option in on the left-hand side.

From here, select the Add Permission icon.

You can now select the domain of the user or group you wish to add from the drop-down list and begin to type the name of that user or group.  It will begin to narrow your selection as you type. 

Select the user.

Now select the appropriate role and select the ‘Propagate to child object’ option.

If you have multiple users, groups or roles you need to assign, repeat the process.

You will now see both permissions in the Global Permissions menu.  If logged in, you will need to log out and back in for this to take effect. Both scenarios are shown.

If you now return to the Tags & Custom Attributes menu, logged in as a user from either of these groups, you will see that the New, Edit, Delete or Add Permission options for Tags or Tag Categories are now available.

Note: Providing a user or group privileges at the Global Permissions Level and selecting, ‘Propagate to child objects’ will give that user or group the privilege’s on the child objects such as vCenter, Cluster, VM and Datastore. 

This can be useful if you have multiple vCenters in Enhanced Linked Mode (ELM) as you only need to apply it once.

vCenter –

Host –

Datastore –

Further information can be found in the following VMware article which explains how you can grant permissions on a Tag object, rather than at the Global or vCenter Levels to give you further granualar control.

https://docs.vmware.com/en/VMware-vSphere/6.7/com.vmware.vsphere.security.doc/GUID-2199584C-B422-4EEF-9340-5449E1FB7DAE.html

Inheriting any environment can be difficult and full of unknowns, hopefully this could help you if you are experiencing a similar issue!

Thanks for reading!