Add NSX-T Tags To Virtual Machines with PowerShell

Posted by

NSX tags on virtual machines often play a fundamental role in a NSX micro-segmentation security framework. Tags are used as the criteria for security group membership which in turn are used as source or destination in distributed firewall policy rules.

Tagging virtual machines in NSX-T can be done in a number of ways. We essentially have two interfaces to work with; the API and the UI.

VM tagging via the API

The API documentation explains how we can send a request to /api/v1/fabric/virtual-machines?action=update_tags.
A POST request with the following request body will assign the tag “scope: os” and “tag: linux” to a particular virtual machine (identified by the external_id).

    "external_id": "50306b16-8840-5912-b0fd-eca1f1b317bb",     
    "tags": [         
        {"scope": "os", "tag": "linux"}  

Using Postman the request looks like this:

Similarly, we can fetch the currently assigned NSX-T tags by sending a GET request to /api/v1/fabric/virtual-machines. Use the display_name and included_fields parameters to narrow down the result:

It’s important to understand that when tagging virtual machines through the API directly, existing virtual machine tags are replaced with the ones specified in the request body.

VM tagging in the UI

Tagging virtual machines in the NSX Manager UI is very straight forward. Under Inventory > Virtual Machines you click the three vertically aligned dots in front of a virtual machine and choose Edit:

You can now start adding tags which consist of an optional value for Scope and a mandatory value for Tag. Click Save when done.

Tip: If you want to list virtual machines with a certain tag you can make use of the natural language search capability in NSX Manager. For example you can write something like this in the global search bar: virtual machine where tags tag = ‘linux’

The Challenge

All good and well, but as you probably noticed assigning NSX-T tags in one of these ways is done on a per virtual machine basis.

This is problematic in an enterprise environment with thousands of virtual machines where large scale tagging operations are a requirement. Imagine a micro-segmentation project where large groups of virtual machines need to be zoned and isolated based on tags (OS tags, tier tags, business group tags, etc). You can’t start doing this per VM and still expect to meet your project’s deadline.

And then tagging via the REST API directly using a tool like Postman has the additional problem that it replaces existing tags so extra care needs to be taken to ensure the desired result.

The Solution

So, it’s pretty obvious that large scale tagging of virtual machines in NSX-T isn’t supposed to be done using the methods mentioned above.

Instead you could let some automation/orchestration tool take care of it. Terraform’s NSX-T provider can do it without too much hassle by using the nsxt_vm_tags resource. There are probably others.

Then you can always code something yourself. Maybe use one of the NSX-T SDKs Python or JAVA for example. There’s also a Go library for NSX-T and some Go scripts for bulk tagging of virtual machines.

In short, if you have some developer skills you will probably have little trouble putting together something that does virtual machine tagging for you in a satisfying way.


I’m not a developer and I also don’t feel like introducing a new automation/orchestration solution just to be able to bulk tag virtual machines for an NSX-T micro-segmentation project (although I really like Terraform).

Bound by these self-inflicted constraints I’m left with scrambling together bits and pieces and try to make the best out of it.

A PowerShell script

This is more or less the reason behind the little PowerShell script nsxt_add_vm_tag.ps1 seeing the light of day. Combining PowerCLI and the NSX-T REST API to get the task done:

Note: You need PowerShell Core with the VMware PowerCLI Core module for this script to work.

Script walkthrough

After gathering the values for some variables including the $newtag and optionally $newscope, the script starts with some PowerCLI to query vCenter for the virtual machines that should be added to the scope:

Connect-VIServer -Server $vcenter -Credential $vccred
$vms = Get-Cluster $vccluster | Get-VM | ForEach-Object { $_ | Get-View }

Criteria can of course be added to Get-VM to narrow the result to VM names, VM folders, VMs with certain vSphere tags, and so on.

Next we enter a ForEach loop where the magic is performed. The InstanceUuid is extracted for each virtual machine. This is used by NSX-T where it’s called external_id:

foreach ($vm in $vms) {
    $vmid = $vm.Config.InstanceUuid

The whole goal here is to add tags, not replace existing ones. The part below gets hold of the currently assigned tags. Some regex matching is used to polish the data so it contains just the relevant parts:

$geturl = "https://$nsxmanager/api/v1/fabric/virtual-machines?external_id=$vmid&included_fields=tags"
$getrequest = Invoke-RestMethod -Uri $geturl -Authentication Basic -Credential $nsxcred -Method Get -ContentType "application/json" -SkipCertificateCheck
$getresult = $getrequest.results | ConvertTo-Json -Compress
$currenttags = [regex]::match($getresult,'\[([^\)]+)\]').Groups[1].Value

We prepare the JSON::

$JSON = @"
    {"external_id":"$vmid","tags":  [{"scope":"$newscope","tag":"$newtag"},$currenttags]}

And finally we POST the new tag plus the already assigned tags to the API:

Invoke-RestMethod -Uri $posturl -Authentication Basic -Credential $nsxcred -Method Post -Body $JSON -ContentType "application/json" -SkipCertificateCheck

The script in action

Some screens where you see how it works:

And the result in NSX Manager shows that the new tag is added while the existing tag is still there:


Beautiful, right? No, but it seems to accomplish the simple task of adding NSX-T tags to a large number of virtual machines in one sweep.

I’ve uploaded this script to Github so you can easily access it. I’m sure it can be improved in many ways and I encourage you to do so!

A final word. There might me different and better approaches to achieve the same result. If you know of one I would be very interested to learn more about it so please let me know 🙂


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.