VMware Cloud Community
JBartholomew
Enthusiast
Enthusiast
Jump to solution

Exporting VM Tags in readable format

So I have been working on a script to import Tags from a CSV into vCenter so we can do batch imports of our vCenter tags (its much easier to edit 1000 rows of a spreadsheet than it is to do it directly in vCenter.)

See: Re: Working with Batch Tags in PowerCLI for the script itself.

The problem I have run into is I now have the import script running properly, but now I require a way to export the updated data to a readable CSV file so I can ensure the data being input is up to date. The issue with the export from powerCLI is how vCenter stores tags. within vCenter, tags are stored as TagCategory/TagName together as an object called $Tag and each $Tag is stored in a different element of the array Get-tagassignment. The problem is the input CSV is split out with each Line consisting of the VMName and which Tags it contains under each category with each category being a column.

so The CSV is as follows:

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

VM1            |                         |       Tag Value     |                           |     Tag Value         |

VM2            |     Tag Value      |      Tag Value      |     Tag Value        |                            |

Where the tags in VMWare are stored as follows:

VMName     |     Category/Tag        |

VM1            |     Category 2/Tag     |

VM1            |     Category 4/Tag     |

VM2           |     Category 1/Tag      |

VM2           |     Category 2/Tag      |

VM2           |     Category 3/Tag      |

So I am looking for a way to get the current output into the format I need, so I can basically create a cyclic loop so the updated tags can be exported back to the CSV so when the CSV is updated it already reflects any changes (such as additional VMs or Removed VMs)

1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Try like this

$tagCat = @()

$tagTab = @{}

foreach($tag in (Get-VM | Get-TagAssignment)){

    $tagCat += $tag.Tag.Category.Name

    $key = $tag.Entity.Name

    if($tagTab.ContainsKey($key)){

    ` $val = $tagTab.Item($key)  

    }

    else{

        $val = @{}

    }

    $val.Add($tag.Tag.Category.Name,$tag.Tag.Name)

    $tagTab[$key] = $val

}

$tagCat = $tagCat | Sort-Object -Unique

$tags = foreach($row in ($tagTab.GetEnumerator() | Sort-Object -Property Key)){

    $obj = New-Object PSObject -Property @{

        VM = $row.Key

    }

    $tagCat | %{

        $obj | Add-Member -Name $_ -Value $row.Value[$_] -MemberType NoteProperty

    }

    $obj

}

$tags | Export-Csv tags.csv -NoTypeInformation -UseCulture


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

View solution in original post

42 Replies
LucD
Leadership
Leadership
Jump to solution

Try something like this

foreach($vm in (Import-Csv tags.csv -Delimiter '|')){

    $vm | Get-Member -MemberType NoteProperty -Name 'Category*' | %{

        if("$($vm.$($_.Name))"){

            New-Object -TypeName PSObject -Property @{

                VMName = $vm.VMName

                'Category/Tag' = "$($_.Name)/$($vm.$($_.Name))"

            }

        }

    }

}


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

Reply
0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

Luc,

Thank you for getting back to me so quickly. It appears from what I can gather from the code, that it is the opposite of what I am looking to accomplish.

I have added a sample of the CSV file below for a little more clarity. The issue I am having is that I need I way to export from powercli all of the VMs and tags  in vmware in the format of the CSV so the CSV can be edited and imported back into to vmware with the tagging script I had written.

Sample CSV

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

Name,Environment,Client,Project

Desktop - Bob,Desktop,Internal,

Server- Web01,Prod,BigCarCompany,Website

Server- MYSQL01,DEV,,Database

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

The issue is, that without the ability to export the list of VMs with Tags, any machines that are added to vCenter will not be reflected in the CSV and will therefor never have their tags updated/added.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Sorry, but that seems to be a completely different CSV as the one in your original question.

I started from this

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

VM1    |            | Tag Value  |            | Tag Value  |

VM2    | Tag Value  | Tag Value  | Tag Value  |            |


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

Reply
0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

my apologies, I realized after your original post my mistake. that was a visualization of how the CSV looked and not a representation of the CSV itself.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Just to make sure I understand the question, can you include an extract of the input CSV and the format in which you want the output to be.


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

Reply
0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

Here is the extract of the CSV

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

Name,Environment,Client,Project

Desktop - Bob,Desktop,Internal,

Server- Web01,Prod,BigCarCompany,Website

Server- MYSQL01,DEV,,Database

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

and that is the format I need powercli to output in as well. within the CSV the first row (Column Headings) is what becomes the Tag Categories (Name,Environment,Client,Project)

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Sorry, but I still don't get what you want as ouput.

In the beginning of the thread you said this should be like

VM1,Name/Desktop-Bob

VM1,Environment/Desktop

VM1,Client/Internal

VM2,Name/Server-Web01

VM2,Environment/Prod

VM2,Client/BigCarCompany

VM2,Project/Website

Is that correct ?

If yes, where do I get the VM names (VM1,VM2...) ?

Or do I compose that list from what is actually there, and not from the input CSV ?


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

Reply
0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

again, my apologies, I find web forums are difficult to explain things some times.

the output:

VM1,Name/Desktop-Bob

VM1,Environment/Desktop

VM1,Client/Internal

VM2,Name/Server-Web01

VM2,Environment/Prod

VM2,Client/BigCarCompany

VM2,Project/Website

is how VMware stores the tags in the internal database (ie.) how they appear if you do a 'Get-tagassignment'), which is not what I need for the export. I am trying to come up with a way to take that output and turn it into the output shown in the sample CSV

ie.)

Name,Environment,Client,Project

Desktop - Bob,Desktop,Internal,

Server- Web01,Prod,BigCarCompany,Website

Server- MYSQL01,DEV,,Database

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Ok, now I got it.

Hold on, I'll convert the script


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

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Try like this

$tagCat = @()

$tagTab = @{}

foreach($tag in (Get-VM | Get-TagAssignment)){

    $tagCat += $tag.Tag.Category.Name

    $key = $tag.Entity.Name

    if($tagTab.ContainsKey($key)){

    ` $val = $tagTab.Item($key)  

    }

    else{

        $val = @{}

    }

    $val.Add($tag.Tag.Category.Name,$tag.Tag.Name)

    $tagTab[$key] = $val

}

$tagCat = $tagCat | Sort-Object -Unique

$tags = foreach($row in ($tagTab.GetEnumerator() | Sort-Object -Property Key)){

    $obj = New-Object PSObject -Property @{

        VM = $row.Key

    }

    $tagCat | %{

        $obj | Add-Member -Name $_ -Value $row.Value[$_] -MemberType NoteProperty

    }

    $obj

}

$tags | Export-Csv tags.csv -NoTypeInformation -UseCulture


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

JBartholomew
Enthusiast
Enthusiast
Jump to solution

Luc, you are a savior. That is exactly what I was looking for.

My issue is that I understand how arrays work, and how they are logically constructed, but I struggled to figure out the coding behind it to get the results I needed.

The only confusion I still have is the name Column is prefixing all of the names with "ClusterInvariantVMMId Veeam Status com.vmware.vdp2.is-protected com.vmware.vdp2.protected-by" and then the VM name.

I see that $key is assigned $Tag.Entity.Name which is just the name of the VM so I am not sure where the rest of that name is coming in..

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

When you do a Get-VM, you see the correct name, right ?

What if you do

Get-VM | Get-TagAssignment |

Select @{N='VM';E={$_.Entity.Name}}

If you see that same wrong name as well, I would need to have a look at the properties.

Do something like

Get-VM | Get-TagAssignment |

Select -ExpandProperty Entity

This might be a PowerCLI "feature".

Which PowerCLI version are you using ?


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

Reply
0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

I have tested this on both PowerCLI 5.5 and PowerCLI 6.0R3 with the same results.

I can see that the $Tags object has the clusterInvariantVMMId..... name for the VMname and the $Tagtab.keys also has it for each object.

I am using PowerGUI to help troubleshoot the script and am seeing a very weird thing when I attempt to pin down where this extraneous information is coming from.

If I do a Get-VM | Get-Tagassignment I do see the ClusterInvariantVMMId... and if I assign the script to a variable to better track the issue ie.) $VMInfo = Get-VM | Get-Tagassignment I see that each object has the ClusterInvariantVMMId as well, but if I expand the syncroot for the object I can see the Entity has the ClusterInvariantVMMId, and if I expand the Entity itself, the name magically changes to the correct name and drops the ClusterInvariantVMMId...

Reply
0 Kudos
JBartholomew
Enthusiast
Enthusiast
Jump to solution

I have pinned down the issue. its in the line

foreach($tag in (Get-VM | Get-TagAssignment)){

The Recursion of the Get-VM | Get-TagAssignment is unnecessary and was actually introducing the incorrect name to the system.

Thank you again Luc for your assistance and enlightenment in PowerCLI

Below is the updated correct code block for future reference:

$tagCat = @()

$tagTab = @{}

foreach($tag in (Get-TagAssignment)){

    $tagCat += $tag.Tag.Category.Name

    $key = $tag.Entity.Name

    if($tagTab.ContainsKey($key)){

    ` $val = $tagTab.Item($key)  

    }

    else{

        $val = @{}

    }

    $val.Add($tag.Tag.Category.Name,$tag.Tag.Name)

    $tagTab[$key] = $val

}

$tagCat = $tagCat | Sort-Object -Unique

$tags = foreach($row in ($tagTab.GetEnumerator() | Sort-Object -Property Key)){

    $obj = New-Object PSObject -Property @{

        VM = $row.Key

    }

    $tagCat | %{

        $obj | Add-Member -Name $_ -Value $row.Value[$_] -MemberType NoteProperty

    }

    $obj

}

$tags | Export-Csv tags.csv -NoTypeInformation -UseCulture

Reply
0 Kudos
markshannon
Enthusiast
Enthusiast
Jump to solution

Hi LucD,

great post.

this works brilliantly apart from where a vm tag is configured as multiple cardinality so only selects one tag per category field -  ie. tag category is 'machine information' and there is multiple tags assigned to that category.

so when exported under 'machine information' column it only shows 1 tag.

is their a way to split each tag from a category?

thanks in advance

mark

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

That would be possible, but how would you place that in the result (CSV file).
The columns can' just be the Category names anymore.


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

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

You could try something like this.
Multiple tags belonging to the same Category will be represented as Tag1|Tag2|Tag3 under the Category column header.

$objTemplate = @{

   VM = ''

}

(Get-TagCategory).Name | Sort-Object | ForEach-Object -Process {

   $objTemplate.Add($_,'')

}


Get-TagAssignment|

Group-Object -Property {$_.Entity.Name} |

ForEach-Object -Process {

   $obj = $objTemplate.Clone()

   $obj['VM'] = $_.Name

   $_.Group | Group-Object -Property {$_.Tag.Category.Name} |

   ForEach-Object -Process {

   $cat = $_.Name

   $vTag = $_.Group | Group-Object -Property {$_.Tag.Category.Name} |

   ForEach-Object -Process {

   $_.Group.Tag.Name

   }

   $obj[$cat] = (($vTag | Sort-Object) -join '|')

   }

   New-Object PSObject -Property $obj

} |

Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture


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

Reply
0 Kudos
markshannon
Enthusiast
Enthusiast
Jump to solution

hi

i tried that and got the below:-

Get-TagAssignment : 05/03/2019 06:47:49 Get-TagAssignment com.vmware.vapi.std.errors.internal_server_error {'messages': [com.vmware.vapi.std.localizable_message {'id': vapi.bindings.method.impl.unexpected, 'default_message': Provider method

implementation threw unexpected exception: Read timed out, 'args': [Read timed out]}], 'data':}

At line:8 char:1

+ Get-TagAssignment|

+ ~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [Get-TagAssignment], CisException

    + FullyQualifiedErrorId : VMware.VimAutomation.ViCore.Impl.V1.Service.Tagging.Cis.TaggingServiceCisImpl.GetTagAssignment.Error,VMware.VimAutomation.ViCore.Cmdlets.Commands.Tagging.GetTagAssignment

thanks

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

When you run Get-TagAssignment on it's own, do you get the same error?


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

Reply
0 Kudos