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
}
}
}
}
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?"
}
}
}
}
}
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
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.
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
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?"
}
}
}
}
}