?
Solved

How can I use ConvertTo-HTML in PowerShell to output values from multiple objects in a multi-column fashion?

Posted on 2015-02-09
5
Medium Priority
?
520 Views
Last Modified: 2015-05-06
Folks -

I am trying to use PowerShell to create a very simple (or so I thought) report from VMM 2012 R2 that would help me hunt down VMs configured with Dynamic MAC address on their virtual NICs.  My enterprise uses static MACs across the board.

So, excluding all the wrapper code that would handle E-mailing the report to me, the main bulk of what gathers the info is as follows:

Import-Module VirtualMachineManager
Get-VMMServer myserver.mydomain.lcl

$VMs = Get-VM
foreach ($VM in $VMs)
{
	$VMNIC = $VM | Get-SCVirtualNetworkAdapter
	if ($VMNIC.MACAddressType -eq "Dynamic")
	{
		$VMNamesHTML += ConvertTo-HTML -InputObject $VM -Property Name -Fragment
		$VMNICsHTML += ConvertTo-HTML -InputObject $VMNIC -Property MACAddressType -Fragment

	}
}

ConvertTo-HTML -Body "$VMNameHTML $VMNICHTML" >>output.html

Open in new window


My problem with this solution is the output format.  The ConvertTo-HTML cmdlet lacks the ability to concatenate multiple variables into the body in multi-column form.  I'm looking for someone to help me get this information formatted in a more readable fashion.  Here is what I get from the code above:

Sample Output
For readability sake, the output should look more like this:

Name                               MACAddress
MYVIRTUALMACHINE    00:15:5D:7D:50:01

So in the case of there being multiple VMs and multiple MAC addresses, the information is easily readable without having to keep track of what line goes with what MAC address, as would be the case of everything is formatted in a single column.

This has become enormously frustrating for me.  Searching the net has resulted in massively divergent solutions to similar problems others have experienced.

ConvertTo-HTML has -InputObject and -Properties parameters.  Unfortunately for me, as is the case with most things PowerShell I run into, they're half-baked beyond belief.  -InputObject does not allow for multiple objects to be passed, as it should.  And therefore -Properties, which should be able to select properties from the multiple input objects by simply using an index number, does not.

Thank you in advance for any solutions you might dream up.
0
Comment
Question by:amendala
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
  • 2
5 Comments
 
LVL 82

Expert Comment

by:David Johnson, CD, MVP
ID: 40599700
Something like the following would probably work.. NOT TESTED
Import-Module VirtualMachineManager
Get-VMMServer myserver.mydomain.lcl
$obj = new-object psobject
$VMs = Get-VM
foreach ($VM in $VMs)
{
	$VMNIC = $VM | Get-SCVirtualNetworkAdapter
	if ($VMNIC.MACAddressType -eq "Dynamic")
	{
		Add-Member -InputObject $obj -MemberType NoteProperty -Name "Machine" -Value $VM
        Add-Member -InputObject $obj -MemberType NoteProperty -Name "MAC-Address" -Value $VMNIC.MACAddressType
    }
}
$obj | ConvertTo-HTML -Body $obj > output.html
                                  

Open in new window

0
 
LVL 40

Accepted Solution

by:
footech earned 2000 total points
ID: 40599802
I think there's a few reasons why ConvertTo-HTML doesn't take more than one input object.  I can't really think of any cmdlet that takes more than one (Compare-Object is the closest).  It's just not the way PS works.  If you think of any cmdlet that produces multiple columns (Format-Table, Export-CSV), you'll see it being fed by a stream of objects with the same properties.  The pipeline is one of the most important aspects of PowerShell, there's only one pipeline at a time.  Maybe it's a lack of imagination, but I can't imagine how things would work if you were trying to feed two (or more) pipelines into a single cmdlet.

In any case, the way to approach this is to input objects with the properties you desire into the ConvertTo-HTML cmdlet.  You'll see some variations of David's example, whether you're collecting the objects into an array and then feeding that array into ConvertTo-HTML, or utilizing the pipeline without collecting into an intermediate array.  The important thing is creating the object with the properties you desire (and there are a few techniques for doing that).  Here's one such variation.
Import-Module VirtualMachineManager
Get-VMMServer myserver.mydomain.lcl

$out = @()
$VMs = Get-VM
foreach ($VM in $VMs)
{
	$VMNIC = $VM | Get-SCVirtualNetworkAdapter
	if ($VMNIC.MACAddressType -eq "Dynamic")
	{
            $out += $VMNIC | Select @{n="Name";e={$VM.Name}},MACAddressType
	}
}
$out | ConvertTo-HTML | Out-File output.html

Open in new window

0
 

Author Comment

by:amendala
ID: 40601779
I had arrived at a partially working solution similar to David's on my own yesterday after much messing around but ran into a problem in that if there are multiple matches, you can't call Add-Member repeatedly because a member of the same name already exists and therefore you end up with a broken script.

So I created an array and in the loop, had a new object created and continued to iterate through the VMs and added the new object to the array.  I then piped that array to ConvertTo-HTML and what it produced was highly repetitive.  Each successive table included the objects before it.

$myArray = @()
$VMs = Get-VM
foreach ($VM in $VMs)
{
	$myObject = New-Object System.Object

	$VMNIC = $VM | Get-SCVirtualNetworkAdapter
	if ($VMNIC.MACAddressType -eq "Dynamic")
	{
		$myObject | Add-Member -type NoteProperty -name Name -value $VM.Name
		$myObject | Add-Member -type NoteProperty -name MACAddress -value $VMNIC.MACAddressType.ToString()

		$myArray += $myObject | Select Name, MACAddress
		$myArray | ConvertTo-HTML >>output.html
	}
}

Open in new window


I need the code to be able to handle multiple matches, create objects as they are encountered, add those objects to the array, and have ConvertTo-HTML render them out in a non-repetitive fashion.

What might I modify in the code above to fix that?
0
 

Author Comment

by:amendala
ID: 40601793
Interestingly enough footech, your code does work precisely as I'd prefer.  I tested it and the output is perfect.  Perhaps I should abandon my solution and look more at yours.  Though I am quite confused (and this is where my frustration with Powershell goes to the moon) at the syntax of:

$out += $VMNIC | Select @{n="Name";e={$VM.Name}},MACAddress

Everything else you coded makes perfect sense and is very elegant.  But that Select has me scratching my eyeballs out.  ;-)

The "@", "n", "e", etc. all make very little sense to me.  I'm rapidly picking up the syntactical quirks of PowerShell but for now, what that means entirely escapes me.  If you can give me a simple breakdown, I could flex this code to do a lot more for me.

Thanks in advance.
0
 
LVL 40

Expert Comment

by:footech
ID: 40601840
out += $VMNIC | Select @{n="Name";e={$VM.Name}},MACAddress
Here's another way to write the above
out += $VMNIC | Select @{name="Name";expression={$VM.Name}},MACAddress
#or
out += $VMNIC | % {  New-Object PsObject -properties @{  Name = $VM.Name; MACAddress = $_.MACAddress } }

Open in new window


Going back to the Select-Object command, the @{n="Name";e={$VM.Name}} part is known as a calculated property.  It is a hash table (hence the @{} notation).  The "n" is short for "Name" (you could also use "Label" or "l") and equals a string value which is the name of the property you are creating (can be anything you want).  "e" is short for "expression" and equals a scriptblock (hence the curly brackets) that evaluates to the desired value.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A brief introduction to what I consider to be the best editor for PowerShell.
There are times when we need to generate a report on the inbox rules, where users have set up forwarding externally in their mailbox. In this article, I will be sharing a script I wrote to generate the report in CSV format.
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles from a Windows Server 2008 domain controller to a Windows Server 2012 domain controlleā€¦
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an antispam), the adminiā€¦
Suggested Courses

801 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question