VMware Cloud Community
hocus_wiz
Contributor
Contributor
Jump to solution

Find VMs with blank tag assignments

I am trying to gather all VMs on a connected vCenter where any one of the tags are missing within the categories of "VM OS TAG","Customer Application", "Customer Application ID", "Customer Service", "Customer Service ID", Divison, Environment,"vRA Customer Group", Location, Brand, DMZ. The logic was to increment tag counter if one of the tags are not set for a VM i.e.

if ($tagCount -gt 0) { $report += $data } else { }

would export that VM info to a master report. However this code still does not pick up all VMs where one of the tags are not set. Moreover a lot of errors for VMs being reported where the tag itself is not assigned for a VM

Clear-Host
#Start date
$sw = [Diagnostics.Stopwatch]::StartNew()
$StartTime = (Get-Date)
$LogTime = Get-Date -Format "dd-MM-yyyy hhmmss"
Write-Host Started at: $StartTime -ForegroundColor Yellow -BackgroundColor Black
$TransFile = "M:\Output\Trans-NoTags-$LogTime.txt"
# if (Test-Path $TransFile) { Remove-Item $TransFile }
Start-Transcript $TransFile -Append -NoClobber

#create CSS style for report
$css = "<title>Files</title>
<style>
table { margin: auto; font-family: Arial; box-shadow: 10px 10px 5px #777; border: black; }
th { background: #000080; color: #eee; max-width: 400px; padding: 5px 10px; }
td { font-size: 10px; padding: 5px 20px; color: #111; }
tr { background: #F0FFFF; }
tr:nth-child(even) { background: #fae6f5; }
tr:nth-child(odd) { background: #e9e1f4; }
</style>"

$report = @()
# $VMs = Get-VM | ? {($_ |Get-TagAssignment) -eq $null}
# $VMs = get-vm | Where-Object { (Get-TagAssignment -Entity $_ -Category "Brand", "Customer*", "Division", "DMZ", "Environment", "Location", "OS", "vRA Customer Group") -eq $null }
$VMs = get-vm
$VMs | ForEach-Object {

$VMTags = Get-TagAssignment -Entity $_
$data = "" | Select-Object VMName, PowerState, "Created date", CPUs, MemoryGB, Storage, vCenter, cluster, "VM OS", "VM OS TAG","Customer Application", "Customer Application ID", "Customer Service", "Customer Service ID", Divison, Environment,"vRA Customer Group", Location, Brand, DMZ
$tagCount = 0
$data.VMName = $_.Name
$data.PowerState = $_.PowerState
$data."Created date" = $_.CreateDate
$data.CPUs = $_.NumCpu
$data.MemoryGB = $_.MemoryGB
$sum = 0
$storage | Out-Null
$storage = Get-VM $_ | Get-HardDisk | ForEach-Object { $sum += $_.Capacitygb }
$data.Storage = $sum
# $data.vCenter = (Get-VM $_).Uid.Split(":")[0].Split("@")[1]
$data.vCenter = (Get-VM $_).ExtensionData.Client.ServiceUrl.Split('/')[2].trimend(":443")

$data.cluster = ($_ | Get-Cluster).Name
$data."VM OS" = ( $_ | Sort-Object | Get-View).Guest.GuestFullName

#region Validate custom tags
if (($VMTags | Where-Object { $_.Tag -like 'OS*' }).Tag.Name) {
$OSTag = ($VMTags | Where-Object { $_.Tag -like 'OS*' }).Tag.Name
}
else {
$OSTag = ""; $tagCount++
}
$data."VM OS TAG" = $OSTag #Tag1

If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[0]) {
$appID = ($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[0]
}
else {
$appID = ""; $tagCount++
}
$data."Customer Application ID" = $appID #Tag2

If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[1]) {
$appName = ($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[1]
}
else {
$appName = ""; $tagCount++
}
$data."Customer Application" = $appName #Tag3

If (($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[0]) {
$svcID = ($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[0]
}
else {
# If ([string]::IsNullOrWhiteSpace($svcID)) { $svcID = ""; $tagCount++ }
$svcID = ""; $tagCount++
}
$data."Customer Service ID" = $svcID #Tag4

If (($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[1]) {
$svcName = ($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[1]
}
else {
$svcName = ""; $tagCount++
}
$data."Customer Service" = $svcName #Tag5
#endregion

If (($VMTags | Where-Object { $_.Tag -like 'Division*' }).Tag.Name) {
$DivData = ($VMTags | Where-Object { $_.Tag -like 'Division*' }).Tag.Name
}
else {
$DivData = ""; ; $tagCount++
}
$data.Divison = $DivData #Tag6

If (($VMTags | Where-Object { $_.Tag -like 'Environment*' }).Tag.Name) {
$EnvData = ($VMTags | Where-Object { $_.Tag -like 'Environment*' }).Tag.Name
}
else {
$EnvData = ""; $tagCount++
}
$data.Environment = $EnvData #Tag7

If (($VMTags | Where-Object { $_.Tag -like 'vRA Business Group*' }).Tag.Name) {
$BusGrp = ($VMTags | Where-Object { $_.Tag -like 'vRA Business Group*' }).Tag.Name
}
else {
$BusGrp = ""; $tagCount++
}
$data."vRA Business Group" = $BusGrp #Tag8

If (($VMTags | Where-Object { $_.Tag -like 'Location*' }).Tag.Name) {
$LocData = ($VMTags | Where-Object { $_.Tag -like 'Location*' }).Tag.Name
}
else {
$LocData = ""; $tagCount++
}
$data.Location = $LocData #Tag9

If (($VMTags | Where-Object { $_.Tag -like 'Brand*' }).Tag.Name) {
$BrandData = ($VMTags | Where-Object { $_.Tag -like 'Brand*' }).Tag.Name
}
else {
$BrandData = ""; $tagCount++
}
$data.Brand = $BrandData #Tag10

if (($VMTags | Where-Object { $_.Tag -like 'DMZ*' }).Tag.Name) {
$DmzData = ($VMTags | Where-Object { $_.Tag -like 'DMZ*' }).Tag.Name
}
else {
$DmzData = ""; $tagCount++
}
$data.DMZ = $DmzData #Tag11

# if ($tagCount -gt 0) { Write-Host $data.VMName `t $tagCount; $report += $data } else { }
if ($tagCount -gt 0) { $report += $data } else { }
}

$data = $StartTime -replace ":", "." -replace "/", "."

#region calculate execution time
#End date
$EndTime = (Get-Date)
Write-Host Stopped at: $EndTime -ForegroundColor Yellow -BackgroundColor Black

# Calculate elapsed time
Write-Host $($EndTime - $StartTime)
$ExecutionTime = ($EndTime - $StartTime)

$tabName = "ExecutionTime"
#Create Table object
$table = New-Object system.Data.DataTable "$tabName"

#Define Columns
$col2 = New-Object system.Data.DataColumn Hours, ([string])
$col3 = New-Object system.Data.DataColumn Minutes, ([string])
$col4 = New-Object system.Data.DataColumn Seconds, ([string])

#Add the Columns
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)

#Create a row
$row = $table.NewRow()

#Enter data in the row
$row.Hours = $ExecutionTime.Hours
$row.Minutes = $ExecutionTime.Minutes
$row.Seconds = $ExecutionTime.Seconds

#Add the row to the table
$table.Rows.Add($row)

$table | ConvertTo-Html -Head $css -Property Hours, Minutes, Seconds | Out-File M:\Output\NoTags_VMs_execution_time_table_$data.html
#endregion calculate execution time

$report | Export-Csv -NoClobber -NoTypeInformation "M:\Output\NoTags_VMs_$data.csv"

$datas = "" | Select-Object VMCount, "VM Total Storage", "VM Total CPU", "VM Total Memory GB"
$datas.VMcount = ($report.vmname).count
$sum = 0
$report.storage | ForEach-Object { $sum += $_ }
$datas."VM Total Storage" = [math]::Round($sum, 2)
$sum = 0
$report.CPUs | ForEach-Object { $sum += $_ }
$datas."VM Total CPU" = [math]::Round($sum, 2)
$sum = 0
$report.MemoryGB | ForEach-Object { $sum += $_ }
$datas."VM Total Memory GB" = [math]::Round($sum, 2)
$datas | ConvertTo-Html -Head $css -Property "VMCount", "VM Total Storage", "VM Total CPU", "VM Total Memory GB" | Out-File M:\Output\Info_$data.html
Stop-Transcript
$sw.Stop()
$sw.Elapsed
if ($sw) { Remove-Variable sw }


Any help on making this work would be much appreciated!


Best Regards,
Hocus

Tags (3)
Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Something like this perhaps.
The use of the -like operator often produces unexpected results, I prefer the -eq operator.

This assumes that you want select first on the Category name, and then retrieve the Tag in that Category.
If you can have multiple Tags in the same Category, this will not work of course.

  If ($VMTags | Where-Object { $_.Tag.Category.Name -eq 'Customer Application ID' }) {
    $appID = ($VMTags | Where-Object { $_.Tag.Category.Name -eq 'Customer Application ID').Tag.Name
  } else {
    $appID = ""; $tagCount++
  }
  $data."Customer Application ID" = $appID #Tag2
 
  If (($VMTags | Where-Object {  $_.Tag.Category.Name -eq 'Customer Application' }) {
    $appName = ($VMTags | Where-Object { $_.Tag.Category.Name -eq 'Customer Application' }).Tag.Name
  } else {
    $appName = ""; $tagCount++
  }

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

14 Replies
LucD
Leadership
Leadership
Jump to solution

It looks like your test is not correct.
The Tag property, as returned by Get-TagAssignment, is a VMware.VimAutomation.ViCore.Types.V1.Tagging.Tag object, not a simple String.
When you compare it to a String (-like "OS*"), the automatic conversion does a ToString of the Tag object.
This results in a String that contains "<Category>/<Tag>".
By doing a -like "OS*" you are in fact testing if the Category starts with OS.
Is that the intention?
If you want to compare the TagName, you should do

if (($VMTags | Where-Object { $_.Tag.Name -like 'OS*' }).Tag.Name) {

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
hocus_wiz
Contributor
Contributor
Jump to solution

Thanks @LucD , tried that but that resulted in lot more false positives.

At the end of the day here is my end goal based on example data  below, the report should highlight ServerA and ServerB but not ServerC as the first two have tags assigments missing, the tagcount in my original code needs to be 9 for ServerA & ServerB thus not reaching the desired tagcount of 11  & ending up in report .

Get-TagAssignment -Entity "ServerA"
Tag Entity
--- ------
Customer Application/Robotics ServerA
Customer Application ID/APC-90786 ServerA
Customer Service ID/SVC-017911 ServerA
Customer Service/Systematics ServerA
OS/RHEL7 ServerA
Location/Bucharest ServerA
Environment/DevTest ServerA
vRA Customer Group/AI ServerA
Division/HRA ServerA

Get-TagAssignment -Entity "ServerB"
Tag Entity
--- ------
Brand/Stark Ltd ServerB
Customer Application/Schematics ServerB
Customer Service ID/SVC-027911 ServerB
Customer Service/Systematics ServerB
OS/Windows ServerB
Location/London ServerB
Environment/DevTest ServerB
vRA Customer Group/AI ServerB
DMZ/false ServerB


Get-TagAssignment -Entity "ServerC"
Tag Entity
--- ------
Brand/Stark Ltd ServerC
Customer Application/Robotics ServerC
Customer Application ID/APC-90786 ServerC
Customer Service ID/SVC-017911 ServerC
Customer Service/Systematics ServerC
Division/HRA ServerC
Location/Bucharest ServerC
OS/RHEL7 ServerC
Environment/DevTest ServerC
vRA Customer Group/AI ServerC
DMZ/true ServerC

 

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Are you testing on the Category name or the Tag name?
Also, which errors you mentioned earlier are you getting?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
hocus_wiz
Contributor
Contributor
Jump to solution

I am testing if each VM has the category and an associated Tag mapped such as below where Entity ServerX has 3 tags assigned against "Customer Service", "vRA Customer Group" and "Customer Service ID" but nothing against "Customer Application" ,"Customer Application ID", "DMZ" etc

Tag              Entity
--- ------
Customer Service/Test                   ServerX
vRA Customer Group/Automation ServerX
Customer Service ID/SRV-00121    ServerX


Revised code below which shows a bit more promise

Clear-Host
#Start date
$sw = [Diagnostics.Stopwatch]::StartNew()
$StartTime = (Get-Date)
$LogTime = Get-Date -Format "dd-MM-yyyy hhmmss"
Write-Host Started at: $StartTime -ForegroundColor Yellow -BackgroundColor Black
$TransFile = "M:\Output\Trans-NoTags-$LogTime.txt"
# if (Test-Path $TransFile) { Remove-Item $TransFile }
Start-Transcript $TransFile -Append -NoClobber

#create CSS style for report
$css = "<title>Files</title>
<style>
table { margin: auto; font-family: Arial; box-shadow: 10px 10px 5px #777; border: black; }
th { background: #000080; color: #eee; max-width: 400px; padding: 5px 10px; }
td { font-size: 10px; padding: 5px 20px; color: #111; }
tr { background: #F0FFFF; }
tr:nth-child(even) { background: #fae6f5; }
tr:nth-child(odd) { background: #e9e1f4; }
</style>"


$report = @()
#$VMs = Get-VM | ? {($_ |Get-TagAssignment) -eq $null}
# $VMs = get-vm | Where-Object { (Get-TagAssignment -Entity $_ -Category "Brand", "Business*", "Division", "DMZ", "Environment", "Location", "OS", "vRA Business Group") -eq $null }
$VMs = get-vm
$VMs | ForEach-Object {
$VMTags = Get-TagAssignment -Entity $_
$data = "" | Select-Object VMName, PowerState, "Created date", CPUs, MemoryGB, Storage, vCenter, cluster, "VM OS", "VM OS TAG", "Customer Application", "Customer Application ID", "Customer Service", "Customer Service ID", Divison, Environment, "vRA Business Group", Location, Brand, DMZ
$tagCount = $VMTags.Count
$data.VMName = $_.Name
$data.PowerState = $_.PowerState
$data."Created date" = $_.CreateDate
$data.CPUs = $_.NumCpu
$data.MemoryGB = $_.MemoryGB
$sum = 0
$storage | Out-Null
$storage = Get-VM $_ | Get-HardDisk | ForEach-Object { $sum += $_.Capacitygb }
$data.Storage = $sum
# $data.vCenter = (Get-VM $_).Uid.Split(":")[0].Split("@")[1]
$data.vCenter = (Get-VM $_).ExtensionData.Client.ServiceUrl.Split('/')[2].trimend(":443")
$data.cluster = ($_ | Get-Cluster).Name
$data."VM OS" = ( $_ | Sort-Object | Get-View).Guest.GuestFullName

#region Validate custom tags
if (($VMTags | Where-Object { $_.Tag -like 'OS*' }).Tag.Name) {

$OSTag = ($VMTags | Where-Object { $_.Tag -like 'OS*' }).Tag.Name
}
else {
$OSTag = ""
}
$data."VM OS TAG" = $OSTag #1

If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[0]) {
$appID = ($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[0]
}
else {
$appID = ""
}
$data."Customer Application ID" = $appID #2

If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[1]) {
$appName = ($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[1]
}
else {
$appName = ""
}
$data."Customer Application" = $appName #3

If (($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[0]) {
$svcID = ($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[0]
}
else {
# If ([string]::IsNullOrWhiteSpace($svcID)) { $svcID = "" }
$svcID = ""
}
$data."Customer Service ID" = $svcID #4

If (($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[1]) {
$svcName = ($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).Tag.Name[1]
}
else {
$svcName = ""
}
$data."Customer Service" = $svcName #5
#endregion

If (($VMTags | Where-Object { $_.Tag -like 'Division*' }).Tag.Name) {
$DivData = ($VMTags | Where-Object { $_.Tag -like 'Division*' }).Tag.Name
}
else {
$DivData = "";
}
$data.Divison = $DivData #6

If (($VMTags | Where-Object { $_.Tag -like 'Environment*' }).Tag.Name) {
$EnvData = ($VMTags | Where-Object { $_.Tag -like 'Environment*' }).Tag.Name
}
else {
$EnvData = ""
}
$data.Environment = $EnvData #7

If (($VMTags | Where-Object { $_.Tag -like 'vRA Business Group*' }).Tag.Name) {
$BusGrp = ($VMTags | Where-Object { $_.Tag -like 'vRA Business Group*' }).Tag.Name
}
else {
$BusGrp = ""
}
$data."vRA Business Group" = $BusGrp #8

If (($VMTags | Where-Object { $_.Tag -like 'Location*' }).Tag.Name) {
$LocData = ($VMTags | Where-Object { $_.Tag -like 'Location*' }).Tag.Name
}
else {
$LocData = ""
}
$data.Location = $LocData #9

If (($VMTags | Where-Object { $_.Tag -like 'Brand*' }).Tag.Name) {
$BrandData = ($VMTags | Where-Object { $_.Tag -like 'Brand*' }).Tag.Name
}
else {
$BrandData = ""
}
$data.Brand = $BrandData #10

if (($VMTags | Where-Object { $_.Tag -like 'DMZ*' }).Tag.Name) {
$DmzData = ($VMTags | Where-Object { $_.Tag -like 'DMZ*' }).Tag.Name
}
else {
$DmzData = ""
}
$data.DMZ = $DmzData #11

# if ($tagCount -gt 0) { Write-Host $data.VMName `t $tagCount; $report += $data } else { }
if ($tagCount -lt 12) { $report += $data } else { }
Remove-Variable VMTags
Remove-Variable data
}


$timedata = $StartTime -replace ":", "." -replace "/", "."

#region calculate execution time
#End date
$EndTime = (Get-Date)
Write-Host Stopped at: $EndTime -ForegroundColor Yellow -BackgroundColor Black

# Calculate elapsed time
Write-Host $($EndTime - $StartTime)
$ExecutionTime = ($EndTime - $StartTime)


$tabName = "ExecutionTime"
#Create Table object
$table = New-Object system.Data.DataTable "$tabName"

#Define Columns
$col2 = New-Object system.Data.DataColumn Hours, ([string])
$col3 = New-Object system.Data.DataColumn Minutes, ([string])
$col4 = New-Object system.Data.DataColumn Seconds, ([string])


#Add the Columns
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)

#Create a row
$row = $table.NewRow()

#Enter data in the row
$row.Hours = $ExecutionTime.Hours
$row.Minutes = $ExecutionTime.Minutes
$row.Seconds = $ExecutionTime.Seconds

#Add the row to the table
$table.Rows.Add($row)

$table | ConvertTo-Html -Head $css -Property Hours, Minutes, Seconds | Out-File M:\Output\NoTags_VMs_execution_time_table_$timedata.html
#endregion calculate execution time

$report | Export-Csv -NoClobber -NoTypeInformation "M:\Output\NoTags_VMs_$timedata.csv"


$datas = "" | Select-Object VMCount, "VM Total Storage", "VM Total CPU", "VM Total Memory GB"
$datas.VMcount = ($report.vmname).count
$sum = 0
$report.storage | ForEach-Object { $sum += $_ }
$datas."VM Total Storage" = [math]::Round($sum, 2)
$sum = 0
$report.CPUs | ForEach-Object { $sum += $_ }
$datas."VM Total CPU" = [math]::Round($sum, 2)
$sum = 0
$report.MemoryGB | ForEach-Object { $sum += $_ }
$datas."VM Total Memory GB" = [math]::Round($sum, 2)
$datas | ConvertTo-Html -Head $css -Property "VMCount", "VM Total Storage", "VM Total CPU", "VM Total Memory GB" | Out-File M:\Output\info_$timedata.html
Stop-Transcript
$sw.Stop()
$sw.Elapsed
if ($sw) { Remove-Variable sw }

 

Errors in code still persisting (where VM does not have the category assigned - line numbers might not be exact as I had to reformat some lines)

Cannot index into a null array.
At M:\NoTags.ps1:55 char:7

+ If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Cannot index into a null array.

At M:\NoTags.ps1:63 char:7
+ If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Cannot index into a null array.

At M:\NoTags.ps1:71 char:7
+ If (($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).T ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Cannot index into a null array.

At M:\NoTags.ps1:80 char:7
+ If (($VMTags | Where-Object { $_.Tag -like 'Customer Service*' }).T ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Cannot index into a null array.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Those errors are there because you also use the array index in the Where-cause

If (($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[0]) {

That should be (as far as I understand what you are trying to do

If ($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }) {
    $appID = ($VMTags | Where-Object { $_.Tag -like 'Customer Application*' }).Tag.Name[0]
  } else {
    $appID = ""; $tagCount++
  }

But using fixed indexes is rather dangerous, you can never be sure of the order of the entries in the array.
A better solution would be to use another Where-clause


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

hocus_wiz
Contributor
Contributor
Jump to solution

Thanks a lot for your help and patience @LucD . Could you elaborate on the second Where-Object usage please ?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Something like this perhaps.
The use of the -like operator often produces unexpected results, I prefer the -eq operator.

This assumes that you want select first on the Category name, and then retrieve the Tag in that Category.
If you can have multiple Tags in the same Category, this will not work of course.

  If ($VMTags | Where-Object { $_.Tag.Category.Name -eq 'Customer Application ID' }) {
    $appID = ($VMTags | Where-Object { $_.Tag.Category.Name -eq 'Customer Application ID').Tag.Name
  } else {
    $appID = ""; $tagCount++
  }
  $data."Customer Application ID" = $appID #Tag2
 
  If (($VMTags | Where-Object {  $_.Tag.Category.Name -eq 'Customer Application' }) {
    $appName = ($VMTags | Where-Object { $_.Tag.Category.Name -eq 'Customer Application' }).Tag.Name
  } else {
    $appName = ""; $tagCount++
  }

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

kwhornlcs
Enthusiast
Enthusiast
Jump to solution

I took your modified/shows promise version and changed it a bit - leveraging a ForEach loop on an array of required Tag Categories instead of the multiple IF..Else's that were hard coded for the Categories - doing it this way makes it easier to add or remove required tag categories as need suits. I also just toggle a boolean if any required categories are missing rather than summing up how many are there. Assumed the tag categories were cardinal in nature (one from each category per entity) - if multiples are present, you can change the line below

$data.$($rtag) = ($vmTagList | Where-Object {$_.Category.Name -eq $rtag}).Name

to

$data.$($rtag) = ($vmTagList | Where-Object {$_.Category.Name -eq $rtag} | Select -First 1).Name

See if this works for you:

Clear-Host
#Start date
$sw = [Diagnostics.Stopwatch]::StartNew()
$StartTime = (Get-Date)
$LogTime = Get-Date -Format "dd-MM-yyyy hhmmss"
Write-Host Started at: $StartTime -ForegroundColor Yellow -BackgroundColor Black
$TransFile = "M:\Output\Trans-NoTags-$LogTime.txt"
# if (Test-Path $TransFile) { Remove-Item $TransFile }
Start-Transcript $TransFile -Append -NoClobber

#create CSS style for report
$css = "<title>Files</title>
<style>
table { margin: auto; font-family: Arial; box-shadow: 10px 10px 5px #777; border: black; }
th { background: #000080; color: #eee; max-width: 400px; padding: 5px 10px; }
td { font-size: 10px; padding: 5px 20px; color: #111; }
tr { background: #F0FFFF; }
tr:nth-child(even) { background: #fae6f5; }
tr:nth-child(odd) { background: #e9e1f4; }
</style>"

$report = @()
$VMs = get-vm
$VMs | ForEach-Object {
$VMTags = Get-TagAssignment -Entity $_
$data = "" | Select-Object VMName, PowerState, "Created date", CPUs, MemoryGB, Storage, vCenter, cluster, "VM OS", "VM OS TAG", "Customer Application", "Customer Application ID", "Customer Service", "Customer Service ID", Divison, Environment, "vRA Business Group", Location, Brand, DMZ
Clear-Host
#Start date
$sw = [Diagnostics.Stopwatch]::StartNew()
$StartTime = (Get-Date)
$LogTime = Get-Date -Format "dd-MM-yyyy hhmmss"
Write-Host Started at: $StartTime -ForegroundColor Yellow -BackgroundColor Black
$TransFile = "H:\Trans-NoTags-$LogTime.txt"
# if (Test-Path $TransFile) { Remove-Item $TransFile }
Start-Transcript $TransFile -Append -NoClobber

#create CSS style for report
$css = "<title>Files</title>
<style>
table { margin: auto; font-family: Arial; box-shadow: 10px 10px 5px #777; border: black; }
th { background: #000080; color: #eee; max-width: 400px; padding: 5px 10px; }
td { font-size: 10px; padding: 5px 20px; color: #111; }
tr { background: #F0FFFF; }
tr:nth-child(even) { background: #fae6f5; }
tr:nth-child(odd) { background: #e9e1f4; }
</style>"


$report = @()

$requiredTags = @(
"VM OS TAG",
"Customer Application",
"Customer Application ID",
"Customer Service",
"Customer Service ID",
"Divison",
"Environment",
"vRA Customer Group",
"Location",
"Brand",
"DMZ"
)


$vmlist = Get-VM
ForEach ($vm in $vmlist) {
$vmTagList = Get-TagAssignment -Entity $vm | Select-Object -ExpandProperty Tag
$data = " " | Select VMName, PowerState, "Created date", CPUs, MemoryGB, Storage, vCenter, cluster, "VM OS"
$missingTag = $false
$data.VMName = $vm.Name
$data.PowerState = $vm.PowerState
$data."Created date" = $vm.CreateDate
$data.CPUs = $vm.NumCpu
$data.MemoryGB = $vm.MemoryGB
$sum = 0
$storage | Out-Null
$storage = $vm | Get-HardDisk | ForEach-Object { $sum += $_.Capacitygb }
$data.Storage = $sum
$data.vCenter = $vm.ExtensionData.Client.ServiceUrl.Split('/')[2].trimend(":443")
$data.cluster = ($vm | Get-Cluster).Name
$data."VM OS" = ($vm | Get-View).Guest.GuestFullName

#Validate custom tags present and collect
ForEach ($rtag in $requiredTags){
Add-Member -MemberType NoteProperty -InputObject $data -Name $rtag -Value ""
If ($rtag -in $vmTagList.Category.Name){
$data.$($rtag) = ($vmTagList | Where-Object {$_.Category.Name -eq $rtag}).Name
} Else {
$data.$($rtag) = ""
$missingTag = $True
}

}

#If VM is missing one or more tag categories, add to the report
If ($missingTag) { $report += $data}

Remove-Variable vmTagList
Remove-Variable data
}


$timedata = $StartTime -replace ":", "." -replace "/", "."

#region calculate execution time
#End date
$EndTime = (Get-Date)
Write-Host Stopped at: $EndTime -ForegroundColor Yellow -BackgroundColor Black

# Calculate elapsed time
Write-Host $($EndTime - $StartTime)
$ExecutionTime = ($EndTime - $StartTime)


$tabName = "ExecutionTime"
#Create Table object
$table = New-Object system.Data.DataTable "$tabName"

#Define Columns
$col2 = New-Object system.Data.DataColumn Hours, ([string])
$col3 = New-Object system.Data.DataColumn Minutes, ([string])
$col4 = New-Object system.Data.DataColumn Seconds, ([string])


#Add the Columns
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)

#Create a row
$row = $table.NewRow()

#Enter data in the row
$row.Hours = $ExecutionTime.Hours
$row.Minutes = $ExecutionTime.Minutes
$row.Seconds = $ExecutionTime.Seconds

#Add the row to the table
$table.Rows.Add($row)

$table | ConvertTo-Html -Head $css -Property Hours, Minutes, Seconds | Out-File M:\Output\NoTags_VMs_execution_time_table_$timedata.html
#endregion calculate execution time

$report | Export-Csv -NoClobber -NoTypeInformation "M:\Output\NoTags_VMs_$timedata.csv"


$datas = "" | Select-Object VMCount, "VM Total Storage", "VM Total CPU", "VM Total Memory GB"
$datas.VMcount = ($report.vmname).count
$sum = 0
$report.storage | ForEach-Object { $sum += $_ }
$datas."VM Total Storage" = [math]::Round($sum, 2)
$sum = 0
$report.CPUs | ForEach-Object { $sum += $_ }
$datas."VM Total CPU" = [math]::Round($sum, 2)
$sum = 0
$report.MemoryGB | ForEach-Object { $sum += $_ }
$datas."VM Total Memory GB" = [math]::Round($sum, 2)
$datas | ConvertTo-Html -Head $css -Property "VMCount", "VM Total Storage", "VM Total CPU", "VM Total Memory GB" | Out-File M:\Output\info_$timedata.html
Stop-Transcript
$sw.Stop()
$sw.Elapsed
if ($sw) { Remove-Variable sw }

 

hocus_wiz
Contributor
Contributor
Jump to solution

As ever you always outshine in your brilliant solutions @LucD . Thanks for being so helpful; your suggestions & tweaks did the trick and now I have a perfectly working billing script. I have followed your articles / responses and solutions since I started learning PowerShell & PowerCLI and your inputs have always saved the day. I am sure I speak for a lot of others who are in the same boat and benefitted from your time and support. Much appreciated.

Best Regards,

Hocus

 

 

Reply
0 Kudos
wila
Immortal
Immortal
Jump to solution

Hi @kwhornlcs 

FYI, next time your posts end up in the spam queue.. please use the "report" option to notify a moderator instead of reposting several times to try and see what sticks 😉
I have released the first of your post attempts and removed the rest.

--
Wil

| Author of Vimalin. The virtual machine Backup app for VMware Fusion, VMware Workstation and Player |
| More info at vimalin.com | Twitter @wilva
Reply
0 Kudos
hocus_wiz
Contributor
Contributor
Jump to solution

Hi @kwhornlcs,

Thanks for looking into the suggested improvements; they really look good tbh and I shall surely give it a go. Much appreciated for looking into this. Having a customisable set of categories is surely useful and I for one don't really like hard coding stuff but my script was a just testing the waters. Let me give the changes a go and see how they pan out compared to current version. 
Best Regards,
Hocus

Reply
0 Kudos
kwhornlcs
Enthusiast
Enthusiast
Jump to solution

Hi @wila, Apologies on the multiple posts...other than the posts just dropping, is there some way the system indicates they've been put in a spam queue? Will definitely use "report" in the future either way.

Thanks 

Reply
0 Kudos
kwhornlcs
Enthusiast
Enthusiast
Jump to solution

@hocus_wiz  - My pleasure! Hope the script works for you! 🙂

Cheers!

Reply
0 Kudos
hocus_wiz
Contributor
Contributor
Jump to solution

Hey @kwhornlcs 

Did finally finish running the script against our environment and unfortunately it did pick up a lot of false +ves somehow. I validate info using RVtools info as well. Could be because there is a mix of cardinality on some of the tag categories. I did two runs one using single and one to cover multiple but could still see too many VMs tagged on both runs  compared to output from RVtools (using Excel coutblank function) showed complete tag coverage on those specific VMs.

Below are some results for Division and Category

H0cu$-PS (0023) > Get-TagCategory -Name "Division"

Name Cardinality Description
---- ----------- -----------
Division Single 
Division Single 
Division Single 
Division Single 
Division Single 
Division Single 
Division Multiple
Division Multiple
Division Multiple


H0cu$-PS (0024) > Get-TagCategory -Name "Environment"

Name Cardinality Description
---- ----------- -----------
Environment Multiple
Environment Multiple
Environment Multiple
Environment Single
Environment Single
Environment Single
Environment Single
Environment Single
Environment Single

I do understand that current script is a bit repititve and hard-coded but tbh if it does what it says on the tin (for now) I am good but surely appreciate the effort and help mate. I shall try to review this later when brain is a less fogged up. 

Reply
0 Kudos