Wednesday, May 13, 2026

Retrieving tags from NSX-T

In the process of preparing a new NSX-T environment, a request came for a dump of the tags in the production environment so that we could match those in the new one. PowerShell & RESTful API to the rescue!

Note: after working with VMware solutions since 2005 and for the company itself for just over 6 years, I have it ingrained to call it "VMware". Aside from this paragraph, you'll probably never see me write "VMware by Broadcom" or even something as gross as "Broadcom vSphere." Just understand that "by Broadcom" is implied until such time as the tech stack finds new ownership.

At any rate: PowerCLI, the module set for PowerShell that VMware publishes for automating parts/pieces of their software stack, is extremely light in cmdlets for interacting with NSX-T. But NSX-T has a very rich RESTful API, so that's what I'm taking advantage of.

I found several solutions that other folks had written, and for one reason or another, it just wasn't working to create output the way we needed it. So here are a couple of iterations that I wrote. The first dumps a CSV-formatted file that lists the tags and the entities that are associated with them; the second dumps a CSV-formatted file that list the VMs and the tags (if any) that are applied to them.

Get-NsxTags.ps1

function Read-Param { <# generic function to grab input from the user, providing for defaults if there's no entry provided #> param( $prompt, $default ) $value = Read-Host ("$prompt [$default]" -f $default) if (-not $value) { return $default } else { return $value } } function Get-Data { <# Perform a RESTful API call against the NSX-T manager #> param( $nsx, $usr, $secPwd, $apiPath ) $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secPwd)) $credPair = "$($usr):$($password)" $encCreds = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($credPair)) $params = @{ Uri = "$nsx$apiPath" Method = 'GET' Headers = @{ "Authorization" = "Basic $encCreds" "Content-Type" = "application/json" } Body = '{}' SkipCertificateCheck = $true } $results = Invoke-RestMethod @params return $results.results } # All NSX-T API paths "hang" off the base path $ApiBase = "/policy/api/v1" # DFW-related stuff is off the Policies path, below. Note: the "default" domain seems to be the only one we have; technically it's a variable... $ApiTags = "$ApiBase/infra/tags" <# INPUT SECTION #> # The actual URI must have the "https://" prefix $nsxtManager = "https://" + (Read-Param "Enter NSX-T IP or FQDN" "Default VIP") $output = Read-Param "Enter output filepath" "c:\temp\get-nsxtags.csv" $username = Read-Param "Enter username" "defaultusername" $secPwd = Read-Host "Enter password" -AsSecureString <# ALGORITHM NSX provides an API to list the tags, and then individual entries provide the tag name and the scope required to retrieve the effective resources associated with them #> $itemlist=@() #storage for the output list #get all the tags $tags = Get-Data -nsx $nsxtManager -usr $username -secPwd $secPwd -apiPath $ApiTags write-host '.' -NoNewline #loop through the tags and grab the associated resources, appending the resource names to a single string field for later output for($t =0; $t -lt $tags.Length; $t++) { $items = '' if($tags[$t].tagged_objects_count -gt 0) { #if there are no objects associated with the tag, don't try and retrieve them $ApiItems = $ApiTags + '/effective-resources?scope=' + $tags[$t].scope + '&tag=' + $tags[$t].tag write-host '.' -NoNewline #this will take a while; show progress happening... $itemSet = Get-Data -nsx $nsxtManager -usr $username -secPwd $secPwd -apiPath $ApiItems foreach ($item in $itemSet) { switch ($item.target_type) { 'VirtualMachine' { $items += $item.target_display_name + ' [vm],' } 'HostTransportNode' { $items += $item.target_display_name + ' [Host],' } default { $items += $item.target_display_name + '<<undefined>>,' } } } } #add a row to the output, removing a trailing comma if needed $row = New-Object PSObject -Property @{ Tag = $tags[$t].tag Count = $tags[$t].tagged_objects_count Items = '' } if ($items.Length -gt 0) { $row.Items = $items.substring(0, $items.length-1) } $itemlist += $row } write-host '.' write-host 'Done' $itemlist | Select-Object 'Tag','Count','Items' | Export-CSV -Path $output -NoTypeInformation


Get-NSXvmTags.ps1

$dfltNSXMGR = "default" $nsxtManager = Read-Host ("Enter NSX-T IP or FQDN [$dfltNSXMGR]" -f $dfltNSXMGR) if (-not $nsxtManager) { $nsxtManager = "https://$dfltNSXMGR" } else { $nsxtManager = "https://$nsxtManager" } $output = Read-Host "Enter output filepath" $username = Read-Host "Enter username" $secPwd = Read-Host "Enter password" -AsSecureString $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secPwd)) $response = Invoke-RestMethod -Uri "$nsxtManager/api/v1/fabric/virtual-machines?included_fields=display_name,tags" -Method Get -Headers @{ "Authorization" = "Basic $( [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("${username}:${password}"))) )" "Content-Type" = "application/json" } -Body '{}' -SkipCertificateCheck $vmlist = @(); foreach ($vm in $response.results) { $tags = '' foreach ($tag in $vm.tags) { $tags += $tag.tag + ',' } if ($tags.Length -gt 0) { $row = New-Object PSObject -Property @{ VM = $vm.display_name Tags = $tags.substring(0, $tags.length-1) } } else { $row = New-Object PSObject -Property @{ VM = $vm.display_name Tags = '' } } $vmlist += $row } $vmlist | Select-Object 'VM','Tags' | Export-CSV -Path $output -NoTypeInformation

These two are interesting in that in the first case, you use one API URI to get the tags—which provide the information (scope, tag) to use as parameters—and a child URI to get the items that are associated with that tag. But in the second case, a single URI provides a list of VMs as well as the tags that are applied to them as properties.

No comments:

Post a Comment