VMware vSphere Tags and Custom Attributes Guide: Efficiently Classify and Manage Hundreds of Virtual Machines

VMware tutorial - IT technology blog
VMware tutorial - IT technology blog

The Pain of Managing Hundreds of VMs Without a Classification System

I manage a VMware cluster with 8 ESXi hosts at my company, and there was a period that gave me real headaches: the number of VMs jumped from 40 to nearly 200 in just six months. Every team named their VMs however they liked — the backend team used api-prod-01, the data team used spark-node-3, the QA team used test-env-ubuntu-22. No common convention whatsoever.

The real-world consequences: whenever someone asked “which VM is running production for the payment team?”, I had to go through each name one by one, or track down each team lead. Incredibly time-consuming. Worse, when we urgently needed to apply a security patch, we had no idea which VMs belonged to which environment to prioritize.

Sound familiar? This is a common situation at many companies where VMware infrastructure grows rapidly without a metadata management strategy in place from the start.

Why VM Names Alone Aren’t Enough

Many people think that following a naming convention for VMs is sufficient. Something like {env}-{team}-{app}-{number}. This seems reasonable, but there are a few real problems:

  • Names can’t carry much dimensional information: A single VM might be production, belong to team A, run as tier 1, and fall within a Friday maintenance window. Cramming all of that into the name produces an unreadable string of characters.
  • Searching by multiple criteria simultaneously is difficult: vSphere Client doesn’t have powerful full-text search. Filtering for “all powered-off production VMs from the backend team” simply isn’t possible with names alone.
  • There’s nowhere to store supplementary information: Ticket numbers, owner names, cost centers, license expiration dates — where do these go? In a comment? In a separate spreadsheet? Both carry the risk of going stale and falling out of sync with reality.
  • Folders in vSphere are one-dimensional: A VM can only be in one folder at a time. You can’t classify it by multiple criteria simultaneously.

Common Approaches People Use

Option 1: Spreadsheet for VM Inventory Management

I’ve seen this at many SME companies. An Excel file with columns for VM name, IP, owner, environment, notes… The problem is it has no direct connection to vSphere, so it frequently goes stale. VMs get deleted but still appear in the spreadsheet; new VMs get created but no one updates the file.

Option 2: Using vSphere Folder Structure

Create folders by team name or environment and place VMs inside. This is better than a spreadsheet, but as mentioned, each VM can only live in one folder, so you can only classify by a single dimension.

Option 3: vSphere Tags + Custom Attributes — My Current Approach

This is VMware’s native solution, built directly into vSphere and working exceptionally well with PowerCLI. Tags let you assign multiple labels to a single VM, while Custom Attributes let you store arbitrary key-value information.

Using vSphere Tags: Multi-Dimensional Labeling for VMs

Tags in vSphere follow a Category → Tag model. Each Category represents a classification dimension, and each Tag is a value within that dimension. For example: the “Environment” Category contains Tags like “Production”, “Staging”, “Development”.

The great thing is that a VM can receive tags from multiple different categories at the same time — completely unlike folders.

Creating Categories and Tags with PowerCLI

# Connect to vCenter
Connect-VIServer -Server vcenter.company.local -User [email protected] -Password "P@ssword"

# Create the Environment category (each VM can only have one tag from this category)
New-TagCategory -Name "Environment" -Cardinality Single -EntityType VirtualMachine

# Create tags in the Environment category
New-Tag -Name "Production" -Category "Environment"
New-Tag -Name "Staging"    -Category "Environment"
New-Tag -Name "Development" -Category "Environment"

# Create the Team category (allows multiple tags if the VM is shared by multiple teams)
New-TagCategory -Name "Team" -Cardinality Multiple -EntityType VirtualMachine

New-Tag -Name "Backend"  -Category "Team"
New-Tag -Name "Frontend" -Category "Team"
New-Tag -Name "Data"     -Category "Team"
New-Tag -Name "DevOps"   -Category "Team"

# Create the Tier category to rank importance levels
New-TagCategory -Name "Tier" -Cardinality Single -EntityType VirtualMachine

New-Tag -Name "Tier1-Critical" -Category "Tier"
New-Tag -Name "Tier2-Important" -Category "Tier"
New-Tag -Name "Tier3-Normal"   -Category "Tier"

Assigning Tags to VMs

# Assign tags to a specific VM
$vm = Get-VM -Name "api-payment-01"
New-TagAssignment -Entity $vm -Tag (Get-Tag -Name "Production" -Category "Environment")
New-TagAssignment -Entity $vm -Tag (Get-Tag -Name "Backend" -Category "Team")
New-TagAssignment -Entity $vm -Tag (Get-Tag -Name "Tier1-Critical" -Category "Tier")

# Bulk assignment: all VMs with names starting with "api-" are tagged as Production
Get-VM | Where-Object { $_.Name -like "api-*" } | ForEach-Object {
    New-TagAssignment -Entity $_ -Tag (Get-Tag -Name "Production" -Category "Environment")
}

Searching VMs by Tag

This is where tags really shine. Instead of scrolling through a list of 200 VMs, filter instantly with a command:

# Get all Production VMs
Get-VM | Where-Object {
    (Get-TagAssignment -Entity $_ | Select-Object -ExpandProperty Tag).Name -contains "Production"
}

# Faster approach using Get-TagAssignment directly
$prodVMs = (Get-TagAssignment -Tag (Get-Tag -Name "Production")).Entity

# Filter VMs that are both Production and in the Backend team
$prodVMs = (Get-TagAssignment -Tag (Get-Tag -Name "Production")).Entity
$backendVMs = (Get-TagAssignment -Tag (Get-Tag -Name "Backend")).Entity
$target = $prodVMs | Where-Object { $backendVMs -contains $_ }
$target | Select-Object Name, PowerState

Custom Attributes: Storing Supplementary Information Per VM

Tags are great for classification and bulk filtering, but they’re not suited for storing free-form text. Custom Attributes are the right place to store things like: owner name, ticket number, deployment date, cost center.

Creating Custom Attributes

# Create custom attributes for VMs
New-CustomAttribute -Name "Owner"       -TargetType VirtualMachine
New-CustomAttribute -Name "Cost Center" -TargetType VirtualMachine
New-CustomAttribute -Name "Ticket"      -TargetType VirtualMachine
New-CustomAttribute -Name "Deploy Date" -TargetType VirtualMachine
New-CustomAttribute -Name "Note"        -TargetType VirtualMachine

Setting and Reading Custom Attribute Values

# Assign information to a VM
$vm = Get-VM -Name "api-payment-01"
Set-Annotation -Entity $vm -CustomAttribute "Owner"       -Value "Nguyen Van A"
Set-Annotation -Entity $vm -CustomAttribute "Cost Center" -Value "IT-PAYMENT-001"
Set-Annotation -Entity $vm -CustomAttribute "Ticket"      -Value "JIRA-2891"
Set-Annotation -Entity $vm -CustomAttribute "Deploy Date" -Value "2025-03-15"

# Read information
Get-Annotation -Entity (Get-VM "api-payment-01")

# Export a full VM report with custom attributes
Get-VM | ForEach-Object {
    $vm = $_
    $annotations = Get-Annotation -Entity $vm
    [PSCustomObject]@{
        VMName      = $vm.Name
        PowerState  = $vm.PowerState
        Owner       = ($annotations | Where-Object {$_.Name -eq "Owner"}).Value
        CostCenter  = ($annotations | Where-Object {$_.Name -eq "Cost Center"}).Value
        Ticket      = ($annotations | Where-Object {$_.Name -eq "Ticket"}).Value
    }
} | Export-Csv -Path "vm-inventory.csv" -NoTypeInformation -Encoding UTF8

Combining Tags and Custom Attributes in Practice

Here’s the model I currently use across my 8 ESXi hosts:

  • Tags for quick classification and bulk filtering: Environment, Team, Tier, OS, Maintenance Window
  • Custom Attributes for identity information: Owner, Cost Center, Ticket, SLA Level

A real-world example: during a scheduled Friday maintenance window, I just run a filter for VMs tagged with “Maintenance-Friday” and get exactly the list of VMs to shut down. No need to remember individual names or look anything up in a spreadsheet.

One more point: if your company uses vRealize Operations or Aria Operations, tags from vSphere are automatically pulled into cost reports and capacity planning — very convenient when you need to allocate costs by team or project.

Deployment Checklist for Teams Just Getting Started

  1. Hold a team meeting to define the required classification dimensions (Environment, Team, Tier are the minimum set)
  2. Create Tag Categories and Tags before creating any new VMs
  3. Retroactively assign tags to existing VMs using a PowerCLI script (takes a few hours but is absolutely worth it)
  4. Establish a policy: all newly created VMs must have the Environment tag and Owner attribute assigned before handoff
  5. Periodically run a script to check for VMs missing tags to keep your inventory clean
# Script to check for VMs missing the Environment tag
Get-VM | ForEach-Object {
    $tags = Get-TagAssignment -Entity $_ | Select-Object -ExpandProperty Tag
    $hasEnv = $tags | Where-Object { $_.Category.Name -eq "Environment" }
    if (-not $hasEnv) {
        Write-Output "[MISSING TAG] $($_.Name) — missing Environment tag"
    }
}

Share: