Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 603
  • Last Modified:

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

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
amendala
Asked:
amendala
  • 2
  • 2
1 Solution
 
David Johnson, CD, MVPOwnerCommented:
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
 
footechCommented:
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
 
amendalaAuthor Commented:
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
 
amendalaAuthor Commented:
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
 
footechCommented:
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

New Tabletop Appliances Blow Competitors Away!

WatchGuard’s new T15, T35 and T55 tabletop UTMs provide the highest-performing security inspection in their class, allowing users at small offices, home offices and distributed enterprises to experience blazing-fast Internet speeds without sacrificing enterprise-grade security.

  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now