Add or remove Tags in NSX-T with PowerCLI

By | 22. December 2019

This post is also available in: German

In this Blogpost I will explain how to add or remove a Tag in NSX-T.

If the VM is not tagged before it is very easy to set a new Tag, but if the VM has already been tagged and you don’t want to loose the existing Tags, it becomes more complicated, because the NSX API will overwrite all Tags if a new one will be set.

If you would like to learn how to use PowerCLI with NSX-T Policy API you can read my Blog: Creating Objects in NSX-T Policy API with VMware PowerCLI

6. March 2020 !!!Important Information!!! The Script discover only the first 1.000 VMs. I am in contact with the Product Managers to find a Solution to discover more than 1.000 VMs and as soon if I have a Solution, I will update this post.

Complete Script

This Script will add a “quarantine” Tag to a VM, with interactive User Input to define which VM should be tagged. The VM name is Case Sensitive and can be found in NSX Inventory -> Virtual Machines.

$display_name = Read-Host -Prompt 'VM Name'
$SecTag = "quarantine"
$secscope = ""
if ($vmdataentrytags) {Remove-Variable vmdataentrytags}
$vmdata = Get-NsxtPolicyService -Name com.vmware.nsx_policy.infra.realized_state.enforcement_points.virtual_machines
$vmdatavmid = @([PSCustomObject]$vmdata.list("default").results | select-object -property display_name, external_id | select-string "display_name=$display_name")
$vmdatavmid = $vmdatavmid -replace ("@{display_name=$display_name; ") -replace ("}")
$vmdataid=$vmdatavmid|ConvertFrom-StringData
$vmdataentry = @([PSCustomObject]$vmdata.list("default").results | select-object -property display_name, tags | select-string "display_name=$display_name")
if ($vmdataentry -like '*scope*') {
$vmdataentrytags=$vmdataentry-replace ("@{display_name=$display_name; tags=(\[)struct ") -replace'(\])'
$vmdataentrytags = $vmdataentrytags -replace ("struct ") -replace ("'") -replace ("}}"),("}") -replace (":"),("=") -replace (" ") -replace ("},"),("};")
#$vmdataentrytags = $vmdataentrytags -replace ("{scope=$secscope,tag=$SecTag};") -replace (";{scope=$secscope,tag=$SecTag}")
$vmdataentrytags = @($vmdataentrytags.split(";"))
$vmdataentrytags = $vmdataentrytags -replace ("{") -replace ("}")
}
$vmdataentrytags+="scope=$secscope,tag=$SecTag"
$vmdatacontent = $vmdata.Help.updatetags.virtual_machine_tags_update.Create()
$vmdatacontent.virtual_machine_id = $vmdataid.external_id
foreach ($item in $vmdataentrytags) {
$item=@($item.split(","))
$vmdatatags1=$item|ConvertFrom-StringData
$vmdatatags=$vmdata.Help.updatetags.virtual_machine_tags_update.tags.Element.Create()
$vmdatatags.tag=$vmdatatags1.tag
$vmdatatags.scope=$vmdatatags1.scope
$vmdatacontent.tags.Add($vmdatatags) |Out-Null
}
$vmdata.updatetags("default", $vmdatacontent)

Let dive into the script:

we will create a Variable, and with “Read-Host”, the Input can be tipped in after the “-Prompt” VM Name.

$display_name = Read-Host -Prompt 'VM Name'

In my example I will set a Tag with the Name “quarantine” and no Scope.

$SecTag = "quarantine"
$secscope = ""

We will store the Information from all VMs recognized in NSX to the Variable “$vmdata”.

$vmdata = Get-NsxtPolicyService -Name com.vmware.nsx_policy.infra.realized_state.enforcement_points.virtual_machines

For each VM we will have now a lot of information.

In all examples I will use the Virtual Machine “DEV-DB”

$vmdata.list("default").results 

----output omitted----
guest_info : struct {'computer_name': dev-db, 'os_name': Ubuntu Linux
(64-bit)}
compute_ids : {moIdOnHost:5, hostLocalId:5,
locationId:564d2f4a-05a3-3fc5-6afc-f839afad7dc1,
instanceUuid:52caf68c-335e-e4e9-18b3-221f5ffe0300…}
resource_type : VirtualMachine
external_id : 564d2f4a-05a3-3fc5-6afc-f839afad7dc1
source : struct {'target_display_name': esx2, 'is_valid': True,
'target_type': HostNode, 'target_id':
283c61ef-fcc0-481f-a881-027338e11654}
_last_sync_time : 1577041205593
type : REGULAR
display_name : DEV-DB
power_state : VM_RUNNING
host_id : 283c61ef-fcc0-481f-a881-027338e11654
local_id_on_host : 5
tags : {struct {'scope': 2, 'tag': dev}, struct {'scope': 1, 'tag':
internal}, struct {'scope': 3, 'tag': db}}
----output omitted----

To add a Tag to a Specific VM we need the external VM ID (VM UUID). We will store the VM Display Name and the ID  in a Variable named “$vmdatavmid”

$vmdatavmid = @([PSCustomObject]$vmdata.list(“default”).results | select-object -property display_name, external_id | select-string “display_name=$display_name”)

With select-string “display_name=$display_name” we will choose the right VM.

But the Information cannot be used without some modification, because we only need the ID.

This will be the output from $vmdatavmid

@{display_name=DEV-DB; external_id=564d2f4a-05a3-3fc5-6afc-f839afad7dc1}
So we need to remove “@{display_name=DEV-DB; ” and “}

$vmdatavmid = $vmdatavmid -replace (“@{display_name=$display_name; “) -replace (“}”)

Let us check the output again
$vmdatavmid
external_id=564d2f4a-05a3-3fc5-6afc-f839afad7dc1

The Information is still in the wrong format so we will convert the information into a table:

$vmdataid=$vmdatavmid|ConvertFrom-StringData
Let us check the output from “$vmdataid”
Name                Value
—-                      —–
external_id      564d2f4a-05a3-3fc5-6afc-f839afad7dc1
 

We will now retrieve the existing Tags and Scope from the VM and store the Information in $vmdataentry.

$vmdataentry = @([PSCustomObject]$vmdata.list("default").results | select-object -property display_name, tags | select-string "display_name=$display_name")

This is the Output from “$vmdataentry” and we need to remove a lot of information…

@{display_name=DEV-DB; tags=[struct {'scope': 2, 'tag': dev}, struct {'scope': 1, 'tag': internal}, struct {'scope': 3, 'tag': db}]}
You can remove/replace everything in one line but I make smaller portions to understand what happen with the string.
So I will remove now “@{display_name=DEV-DB; tags=[struct ” and the Bracket “]” at the end.
Brackets will be interpreted by PowerShell and must be isolated with (\]).
 
$vmdataentrytags=$vmdataentry-replace ("@{display_name=$display_name; tags=(\[)struct ") -replace'(\])'

In the next Step we will remove and replace all other information

$vmdataentrytags = $vmdataentrytags -replace ("struct ") -replace ("'") -replace ("}}"),("}") -replace (":"),("=") -replace (" ") -replace ("},"),("};")

Let’s check the Variable “$vmdataentrytags”:

{scope=2,tag=dev};{scope=1,tag=internal};{scope=3,tag=db}

If you would like to remove a Tag/Scope, you need to insert here the following Line:

$vmdataentrytags = $vmdataentrytags -replace ("{scope=$secscope,tag=$SecTag};") -replace (";{scope=$secscope,tag=$SecTag}")

We will split the String now. The Identifier is “;”

$vmdataentrytags = @($vmdataentrytags.split(";"))

…and remove also the curly brackets.

$vmdataentrytags = $vmdataentrytags -replace ("{") -replace ("}")

The output from “$vmdataentrytags” should look like the following output.

scope=2,tag=dev
scope=1,tag=internal
scope=3,tag=db

We will add now our additional Tag/Scope we defined on the Top of the script. If you would like to remove the Tag/Scope you need to comment out this line.

$vmdataentrytags+="scope=$secscope,tag=$SecTag"

The output from “$vmdataentrytags” will have now the additional Tag.

scope=2,tag=dev
scope=1,tag=internal
scope=3,tag=db
scope=,tag=quarantine

The next Variable for NSX-T will apply later the Tags/Scope to a VM.

$vmdatacontent = $vmdata.Help.updatetags.virtual_machine_tags_update.Create()

We will add the Virtual Machine ID (UUID)

$vmdatacontent.virtual_machine_id = $vmdataid.external_id

The API Structure for VM Tags is as follows:

Unfortunately the Tags are not placed in a list so each scope/tag combination must be added as a single entry. We will create a loop (foreach) to attach all Tags.

foreach ($item in $vmdataentrytags) {

We will split now each “$vmdataentrytags” in scope and tag. The identifier is “,”.

$item = @($item.split(","))

…and create a List

$vmdatatags1=$item|ConvertFrom-StringData

We will add now for each entry the Tag/Scope to “$vmdatacontent” and close the loop.

$vmdatatags = $vmdata.Help.updatetags.virtual_machine_tags_update.tags.Element.Create()

$vmdatatags.tag=$vmdatatags1.tag

$vmdatatags.scope=$vmdatatags1.scope
$vmdatacontent.tags.Add($vmdatatags) | Out-Null

}

The Last step will add the Tags to the VM.

$vmdata.updatetags("default", $vmdatacontent)
print
Daniel Stich
Follow me

3 thoughts on “Add or remove Tags in NSX-T with PowerCLI

  1. Pingback: Working with Groups in NSX created with PowerCLI » vrealize.it - TechBlog VMware SDDC

  2. Ethan

    Hello Daniel:
    Thanks for you contribute, May I aska question?
    When I using this script to tags for a VM ,but I got a error as below, could you please help me ?

    ===============================================
    VM Name: P-IT-rhel6x64_157
    userValue
    At line:47 char:1
    + $vmdata.updatetags(“default”, $vmdatacontent)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], CisException
    + FullyQualifiedErrorId : VMware.VimAutomation.Cis.Core.Types.V1.CisException

    Reply
    1. Daniel Stich Post author

      Hello Ethan,

      Thank you for your comment!

      I recognized, that the Script is only working, if the VM has at least one TAG configured. Otherwise the Split will run into a failure.
      I have updated the script now and added:
      if ($vmdataentry -like ‘*scope*’) {
      ….
      }
      If an TAG exist $vmdataentry will have the “scope” in the content. Otherwise neither “scope” nor “tag” will be in the content. And with my if command I make sure that the Script will directly jump to adding a new tag.

      Please check this out and give me a feedback if it is working for you.

      Regards

      Daniel

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

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