VMware Cloud Community
JBartholomew
Enthusiast
Enthusiast
Jump to solution

Working with Batch Tags in PowerCLI

I have recently started in a new position, and have begun to work on cleaning things up in the VMWare environment. A little background, We are a hosting service provider who hosts VMs for various Auto Manufactures. Currently we have approximately 1400 VMs. One of the things I have wanted to do is to add Tags to all of our VMs so we can better organize our VMs,

The goal of this code is to apply the tags in a master CSV into the VMWare environment.

The current issues I am facing are:

- Tags that currently exist, but are incorrect do not get updated

- I cannot seem to figure out a clean way to check if a Tag is already applied to a VM and if so skip trying to apply it.

I am trying to make the code as flexible as possible by not calling Tag categories explicitly so if a category is added in the future, the code does not need to be re-written.

The CSV currently has 4 Columns (Name, Client, Environment, Project)

Here is the code, any suggestions would be greatly appreciated.

# Connect-viserver myvc.corp.local -user administrator@vsphere.local -pass Pa$$w0rd

$VMWareInfo = Import-CSV C:\Temp\VMWareInfo.csv

# Get the header names to use as tag category names

$TagCatNames = $VMWareInfo | Get-Member | Where {$_.MemberType -eq "NoteProperty"} | Select -Expand Name |Sort-Object

$TagCatNames = $TagCatNames.trim()

# Get the currently assigned Tags for the VMs

$CurrentTagAssignment = Get-Tagassignment | Select Entity, tag

# Create the Tag Category if it doesnt exist

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    if (Get-TagCategory $CatName) {Write-Host "Entry" $CatName "exists, Skipping..."}

    if (-Not (Get-TagCategory $CatName -ea SilentlyContinue)) {

        Write-Host "Creating Tag Category $CatName"

        New-TagCategory -Name $CatName -Description "$CatName via Host Naming Convention"

       }

    }

   

# Create Tags under the Tag Categories

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

      $Uniquetags= $VMWareInfo |select -Expand $CatName.Trim() |sort-object -Unique

          #Assign the Tags to the Categories

        Foreach ($TagName in $UniqueTags) {

            #Error checking for blank or null tags

            if ($TagName -eq "" -or $null) {Write-Host "Blank Entry: " $CatName "-" $TagName}

            #Adding New tags to each category

            elseif (-Not (Get-Tag -Category $Catname -Name $TagName -ea SilentlyContinue)) {

                Write-Host "Creating New Tag under $CatName of $TagName"

                New-Tag -Name $TagName -Category $CatName -Description "$TagName via Host Naming Convention"

                }

            #Skip adding if the Tag already exists in the category

            elseif (Get-Tag -Category $CatName -Name $TagName) {Write-Host "Tag Entry Already Exists: " $CatName "-" $tagName 

                }   

               

            #Assigning Tags to VMs/Hosts

        $VMwareinfo | Where {$_.($Name) -eq $TagName} | Foreach {

            #Error checking for blank or null tags

            if ($Name -eq "" -or $null) {Write-Host "Blank Name"}

            elseif ($TagName -eq "" -or $null) {Write-Host "Blank Tag for" $($_.Name)}

            else {Write-Host ".... Assigning $TagName in Category of $CatName to $($_.Name)"

                $TagAssignment = Get-Tag -Category $Name -name $TagName

                New-TagAssignment -entity $($_.Name) -Tag $Tagassignment| out-null

                }

             }   

        }   

    }

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
JBartholomew
Enthusiast
Enthusiast
Jump to solution

so after a bunch of fussing and fighting I finally managed to get the Import tags working. it is not the cleanest script, but it works. so in the effort to ensure that anyone else in the same position as me actually can find an answer here is the script.

The only input to this file is CSV file which is in the format

VMName | Category 1 | Category 2 Category 3 Category 4      |

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Connect-viserver myvc.corp.local -user administrator@vsphere.local -pass Pa$$w0rd

# Import CSV of VM Tags (In the format VMName,Tag,Tag,Tag,etc with Column headers for Tag Categories)

Write-host "Importing CSV File now..."

$VMWareInfo = Import-CSV C:\Temp\VMWareInfo.csv

# Gather all VMs from vCenter to find ones missing tags

Write-Host "Gathering all VMs"

$AllVMs = Get-VM | select name | sort name

# Get the header names to use as tag category names

Write-Host "Gathering Tag Categories"

$TagCatNames = $VMWareInfo | Get-Member | Where {$_.MemberType -eq "NoteProperty"} | Select -Expand Name |Sort-Object

$TagCatNames = $TagCatNames.trim()

# Get the currently assigned tags for the VMs

Write-Host "Gathering Current VM Tag Information"

$CurrentTagAssignment = Get-Tagassignment | Select Entity, tag |Sort-Object entity

# Create the tag category if it doesnt exist

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    if (Get-TagCategory $CatName) {Write-Host "Entry" $CatName "exists, Skipping..."}

    if (-Not (Get-TagCategory $CatName -ea SilentlyContinue)) {

        Write-Host "Creating Tag Category $CatName"

        New-TagCategory -Name $CatName -Description "$CatName via Host Naming Convention"

       }

    }

   

# Create tags under the tag categories

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    $Uniquetags= $VMWareInfo |select -Expand $CatName.Trim() |sort-object -Unique

    # Assign the tags to the categories

    Foreach ($TagName in $UniqueTags) {

        # Error checking for blank or null tags

        if ($TagName -eq "" -or $null) {

            Write-Host "Blank Entry: " $CatName "-" $TagName}   

        # Adding new tags to each category

        elseif (-Not (Get-Tag -Category $Catname -Name $TagName -ea SilentlyContinue)) {

            Write-Host "Creating New Tag under $CatName of $TagName"

            New-Tag -Name $TagName -Category $CatName -Description "$TagName via Host Naming Convention"

            }

        # Skip adding if the tag already exists in the category

        elseif (Get-Tag -Category $CatName -Name $TagName) {Write-Host "Tag Entry Already Exists: " $CatName "-" $tagName "Skipping..."

        }   

    }

}

# Checking tags against currently entered tags

# Checking against each category

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    # Creating list of tags to be checked for current category

    $Uniquetags= $VMWareInfo |select -Expand $CatName.Trim() |where {$_ -notlike $null}| sort-object -Unique

    # Adding line for blank entries

    $Uniquetags+= ""

    # Checking each tag

    foreach ($TagName in $uniquetags) {

    Write-Host "Working on" $CatName"/"$TagName

        # Listing all VMs that have the currently selected tag

        $VMList=$VMwareinfo | Where {$_.($CatName) -eq $TagName}

        foreach ($VMName in $VMList){

            # Find all the VMs that the current tag applies to

            $CurrentVMTags= $CurrentTagassignment |select entity,tag| where { $_.entity -like $VMName.name -and $_.tag.Category -like $CatName}

            # If tag is blank on the spreadsheet

            If($VMName.$CatName -like "" -or $VMName.$CatName -like $null){

                # Check to see if the is blank on the VM

                if ($CurrentVMTags -like "" -or $CurrentVMTags -eq $null){

                    Write-Host "Blank Entry for Category" $CatName "on VM" $VMName.name "Ignoring"

                }

                # If the tag was erroneously written to vCenter

                elseif ($CurrentVMTags -notlike "" -or $CurrentVMTags -notlike $null){

                    Write-Host "Tag erroniously written to VM" $VMName.Name "Removing incorrect entry"

                    Get-TagAssignment -entity $VMName.Name -Category $CatName |Remove-Tagassignment -Confirm:$false

                }

            }

            # If a tag already exists in vCenter

            If(($CurrentVMTags -ne "" -or $CurrentVMTags -ne $null)-and ($VMName.$catname -ne "" -or $VMname.catname -ne $null)) {

                # Skip tag if it already matches

                if (($currentVMTags.Tag.Category -like $catname -and $CurrentVMTags.Tag.name -like $VMname.$catname) -or ($vmname.$catname -like "" -or $vmname.$catname -like $null)) {

                    Write-Host "Written Tag" $CurrentVMTags.Tag "Matches" $CatName"/"$TagName for $Vmname.name "Skipping Entry..."

                }

                # Re-write tag if it does not match

                elseif ($CurrentVMTags.Tag.name -notlike $VMname.$catname -and $currentVMTags.tag.name -ne $null -and $VMname.$canname -ne $null) {

                    Write-Host "Tag Mis-Match for" $VMname.Name " Tags to Write" $CatName"/"$TagName "Written Tag" $CurrentVMTags.tag.Category"/"$CurrentVMTags.tag.name

                    Write-Host "Removing Old Tag" $CurrentVMTags.tag.Category"/"$CurrentVMTags.tag.name

                    Get-TagAssignment -entity $VMName.Name -Category $CatName |Remove-Tagassignment -Confirm:$false

                    Write-Host "Adding New Tag" $CatName"/"$TagName "for VM" $VMName.Name

                    New-TagAssignment -Entity $VMName.Name -Tag $TagName

                }

            }

            # If the tag does not exist, find out if the vm has no tags or doesn't exist

            If (($CurrentVMTags -like "" -or $CurrentVMTags -eq $null) -and ($Vmname.$catname -notlike "" -or $VMname.$catname -notlike $Null)){

                $VMExist=$AllVMs | where {$_.name -Like $VMName.Name}|Measure

                # If the VM no longer exists in vSphere

                if ($VMExist.count -eq 0) {

                    Write-Host $VMName.Name "Not Found in vCenter. Please Remove from Spreadsheet"

                }

                # If the VM exists, but has no tags assigned

                elseif ($VMExist.count -eq 1){

                    Write-Host "VM Tag" $CatName"/"$Tagname "missing from" $VMName.name "Adding Now..."

                    New-TagAssignment -Entity $VMName.Name -Tag $TagName

                }

                # if there are multiple VMs with the same name in vSphere

                elseif ($VMExist.Count -gt 1) {

                    Write-Host "Multiple VMs with the same name Exist. Is" $VMName.Name "a Template?"

                }

            }

        }   

    }

}

View solution in original post

0 Kudos
4 Replies
LucD
Leadership
Leadership
Jump to solution

It's not 100% clear to me how you assign tags to tag categories.

Perhaps some sample input (scramble the actual values), would clarify that.

On your 1st question, to create the TagCategory if it doesn't exist, you can use a Try-Catch construct.

Something like this

# Create the Tag Category if it doesnt exist

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    Try{

        Get-TagCategory -Name $CatName -ErrorAction Stop

        Write-Host "Entry" $CatName "exists, Skipping..."

    }

    Catch{

        Write-Host "Creating Tag Category $CatName"

        New-TagCategory -Name $CatName -Description "$CatName via Host Naming Convention"

    }

}


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

0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

Attached are a few examples from the CSV

This code was based off code from : http://www.virtu-al.net/2014/11/13/automating-tags-tag-category-creation-assignment-powercli/

I took the code and have made adjustments to it to try to make it a little cleaner.

here is what the code does

-Connects to the VCenter Server (Line 1)

-Brings in the CSV file ($VMWareInfo) (Line 2)

-Creates a list of the Headings from the CSV to be used as Tag Categories ($TagCatNames) (Lines 5-6)

-Grabs the current Tags from VMWare ($CurrentTagAssignment) *Not being used anywhere, this was an attempt to move away from checking the VMware system for every tag (which is a slow process)

-Checks the current list of Tag Categories and adds any new ones it finds (Lines 11-18)

This is where is gets very messy

- For each Tag Category

     -it compiles a list of unique tags ($UniqueTags) (Line 22)

     -Then it checks that list of unique tags against VMWares current list of Tags for that category and adds any new ones (Lines 22-34)

     -Then assigns the tag to Each VM that needs it (Lines 37-45)

Adding the Tag Categories, and Tags to VMware aren't the problem. The problem is trying to update tags for the VMs if they already exist

ie.) if a VM has a Environment of QA but it needs to be updated to an Environment of PROD, VMWare won't allow you to just write the new tag, you need to remove the old tag and add the new one. The Problem with this is that I don't want the system to remove and re-add the tags to every machines, as the process takes between 10-20 seconds per tag, hence the second part of the question of how to get VMware to check if the Tags match, and if so, move on.

0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

Here is an update to my original post.

I am still having issues with the original issues posted in the first post

1.) VMs that currently have tags do not get updated (The New-Tagassignment cmdlt only works on empty fields)

     ie.) if a VM has a tag assigned of Environment/Desktop, and I wanted to update it to Environment/Virtual Machine, If I ran new-tagassignment -tag "Virtual Machine" -entity $VMName it would not update the entry.

2.) I cannot seem to find a way to quickly compare the current tags for a VM to the tags that i want to apply so I can work towards only removing/re-adding tags that are not equal to what I want them to be.

The reason for issue #2 is because to call a 'Get-tagassignment' for each VM Tag is incredibly process intensive, as it takes up to 25 seconds per call, and when you extrapolate that out for 1600 VMs, it becomes unreasonable long.

my thought for solving this was to use an array to hold the current VM tags, and then have my list of purposed tags check against that, but the problem I ran into is getting the two arrays to compare.

The new array is called $CurrentTagAssignment and consists of the Entity (VMName) and the Tag and is pulled in with the following command

$CurrentTagAssignment = Get-Tagassignment | Select Entity, tag |Sort-Object entity

my thought was to use something like this to match the two

foreach ($Name in $VMWareInfo) {

    $VMName=$name

    $CurrentTagassignment | where { $CurrentTagAssignment.entity.name -eq $VMName.name}

    }

although it finds the $VMName.name correctly, I cannot seem to find the right naming to get the name from $CurrentTagAssignment.Entity.Name

0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

so after a bunch of fussing and fighting I finally managed to get the Import tags working. it is not the cleanest script, but it works. so in the effort to ensure that anyone else in the same position as me actually can find an answer here is the script.

The only input to this file is CSV file which is in the format

VMName | Category 1 | Category 2 Category 3 Category 4      |

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Connect-viserver myvc.corp.local -user administrator@vsphere.local -pass Pa$$w0rd

# Import CSV of VM Tags (In the format VMName,Tag,Tag,Tag,etc with Column headers for Tag Categories)

Write-host "Importing CSV File now..."

$VMWareInfo = Import-CSV C:\Temp\VMWareInfo.csv

# Gather all VMs from vCenter to find ones missing tags

Write-Host "Gathering all VMs"

$AllVMs = Get-VM | select name | sort name

# Get the header names to use as tag category names

Write-Host "Gathering Tag Categories"

$TagCatNames = $VMWareInfo | Get-Member | Where {$_.MemberType -eq "NoteProperty"} | Select -Expand Name |Sort-Object

$TagCatNames = $TagCatNames.trim()

# Get the currently assigned tags for the VMs

Write-Host "Gathering Current VM Tag Information"

$CurrentTagAssignment = Get-Tagassignment | Select Entity, tag |Sort-Object entity

# Create the tag category if it doesnt exist

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    if (Get-TagCategory $CatName) {Write-Host "Entry" $CatName "exists, Skipping..."}

    if (-Not (Get-TagCategory $CatName -ea SilentlyContinue)) {

        Write-Host "Creating Tag Category $CatName"

        New-TagCategory -Name $CatName -Description "$CatName via Host Naming Convention"

       }

    }

   

# Create tags under the tag categories

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    $Uniquetags= $VMWareInfo |select -Expand $CatName.Trim() |sort-object -Unique

    # Assign the tags to the categories

    Foreach ($TagName in $UniqueTags) {

        # Error checking for blank or null tags

        if ($TagName -eq "" -or $null) {

            Write-Host "Blank Entry: " $CatName "-" $TagName}   

        # Adding new tags to each category

        elseif (-Not (Get-Tag -Category $Catname -Name $TagName -ea SilentlyContinue)) {

            Write-Host "Creating New Tag under $CatName of $TagName"

            New-Tag -Name $TagName -Category $CatName -Description "$TagName via Host Naming Convention"

            }

        # Skip adding if the tag already exists in the category

        elseif (Get-Tag -Category $CatName -Name $TagName) {Write-Host "Tag Entry Already Exists: " $CatName "-" $tagName "Skipping..."

        }   

    }

}

# Checking tags against currently entered tags

# Checking against each category

Foreach ($CatName in ($TagCatNames | Where {$_ -ne "Name"})) {

    # Creating list of tags to be checked for current category

    $Uniquetags= $VMWareInfo |select -Expand $CatName.Trim() |where {$_ -notlike $null}| sort-object -Unique

    # Adding line for blank entries

    $Uniquetags+= ""

    # Checking each tag

    foreach ($TagName in $uniquetags) {

    Write-Host "Working on" $CatName"/"$TagName

        # Listing all VMs that have the currently selected tag

        $VMList=$VMwareinfo | Where {$_.($CatName) -eq $TagName}

        foreach ($VMName in $VMList){

            # Find all the VMs that the current tag applies to

            $CurrentVMTags= $CurrentTagassignment |select entity,tag| where { $_.entity -like $VMName.name -and $_.tag.Category -like $CatName}

            # If tag is blank on the spreadsheet

            If($VMName.$CatName -like "" -or $VMName.$CatName -like $null){

                # Check to see if the is blank on the VM

                if ($CurrentVMTags -like "" -or $CurrentVMTags -eq $null){

                    Write-Host "Blank Entry for Category" $CatName "on VM" $VMName.name "Ignoring"

                }

                # If the tag was erroneously written to vCenter

                elseif ($CurrentVMTags -notlike "" -or $CurrentVMTags -notlike $null){

                    Write-Host "Tag erroniously written to VM" $VMName.Name "Removing incorrect entry"

                    Get-TagAssignment -entity $VMName.Name -Category $CatName |Remove-Tagassignment -Confirm:$false

                }

            }

            # If a tag already exists in vCenter

            If(($CurrentVMTags -ne "" -or $CurrentVMTags -ne $null)-and ($VMName.$catname -ne "" -or $VMname.catname -ne $null)) {

                # Skip tag if it already matches

                if (($currentVMTags.Tag.Category -like $catname -and $CurrentVMTags.Tag.name -like $VMname.$catname) -or ($vmname.$catname -like "" -or $vmname.$catname -like $null)) {

                    Write-Host "Written Tag" $CurrentVMTags.Tag "Matches" $CatName"/"$TagName for $Vmname.name "Skipping Entry..."

                }

                # Re-write tag if it does not match

                elseif ($CurrentVMTags.Tag.name -notlike $VMname.$catname -and $currentVMTags.tag.name -ne $null -and $VMname.$canname -ne $null) {

                    Write-Host "Tag Mis-Match for" $VMname.Name " Tags to Write" $CatName"/"$TagName "Written Tag" $CurrentVMTags.tag.Category"/"$CurrentVMTags.tag.name

                    Write-Host "Removing Old Tag" $CurrentVMTags.tag.Category"/"$CurrentVMTags.tag.name

                    Get-TagAssignment -entity $VMName.Name -Category $CatName |Remove-Tagassignment -Confirm:$false

                    Write-Host "Adding New Tag" $CatName"/"$TagName "for VM" $VMName.Name

                    New-TagAssignment -Entity $VMName.Name -Tag $TagName

                }

            }

            # If the tag does not exist, find out if the vm has no tags or doesn't exist

            If (($CurrentVMTags -like "" -or $CurrentVMTags -eq $null) -and ($Vmname.$catname -notlike "" -or $VMname.$catname -notlike $Null)){

                $VMExist=$AllVMs | where {$_.name -Like $VMName.Name}|Measure

                # If the VM no longer exists in vSphere

                if ($VMExist.count -eq 0) {

                    Write-Host $VMName.Name "Not Found in vCenter. Please Remove from Spreadsheet"

                }

                # If the VM exists, but has no tags assigned

                elseif ($VMExist.count -eq 1){

                    Write-Host "VM Tag" $CatName"/"$Tagname "missing from" $VMName.name "Adding Now..."

                    New-TagAssignment -Entity $VMName.Name -Tag $TagName

                }

                # if there are multiple VMs with the same name in vSphere

                elseif ($VMExist.Count -gt 1) {

                    Write-Host "Multiple VMs with the same name Exist. Is" $VMName.Name "a Template?"

                }

            }

        }   

    }

}

0 Kudos