Private/ConverTo-Html2.ps1

function ConvertTo-Html2 {

    param(    
        [PSCustomObject[]]$objArray,
        [string]$headermsg,
        [switch]$begindoc=$false,
        [switch]$enddoc=$false,
        [string]$footermsg,
        [string]$ok,
        [string]$nok,
        [string]$nak,
        [string]$warn
    )
    <#
    Description:
    Renders a formatted HTML table. Benefits over "convertTo-HTML" are that it can be used to create a doc with multiple tables and has nicer formatting
    Ideally all objects in $objArray will have identical properties to avoid missing data
    Table columns will be established based on the first row of the $objArray
    If subsequent objects have different properties:
    - If fewer columns, corresponding cells for that object's row will be empty
    - If extra columns, those values will be ignored
    #>


    <#
    # Params:
    # objArray: Array of PSCustomObjects and generates an html table as output. If $null, no table will be rendered
    # headermsg: A message that will be displayed above each table in bold, but is not exclusive to the table parameter
    # begindoc: Include html tags to begin an html document (html/head/body). If appending a 2nd table, do not set
    # enddoc: Include html tags to end an html document if closing or adding a final table
    # footermsg: Footer message that would be included after each table, but is not exclusive to the table parameter
    # ok: The text value that indicates to use the "ok" <td> formatting. Also the default format. See below for formatting description.
    # nok: The text value that indicates to use the "nok" <td> formatting. See below for formatting description.
    # nak: The text value that indicates to use the "ok" <td> formatting. See below for formatting description.
    # warn: The text value that indicates to use the "ok" <td> formatting. See below for formatting description.
    #>


    # Data values that will trigger different <td> formatting, good for more fixed output (values such as OK, PASS, FAIL, WARN, NA, etc)
    # If none are passed in, set them to a value that won't accidentally be matched (ie GUID)
    if (! $ok) {$ok=(New-Guid).Guid} #OK - White fill, black text
    if (! $nok) {$nok=(New-Guid).Guid} #FAIL - Red fill, black text
    if (! $nak) {$nak=(New-Guid).Guid} #NA - White fill, grey text
    if (! $warn) {$warn=(New-Guid).Guid} #WARN - Yellow fill, black text

    $crlf = "`r`n"
    $tdhash = @{
        "head"="<td align=`"left`" style=`"border: 1px solid #ddd;background-color: #CCC; padding-left: 6px; padding-right: 6px;`"><b>";
        $ok="<td align=`"left`" style=`"border: 1px solid #ddd;background-color: #FFF; padding-left: 6px; padding-right: 6px;`">";
        $nok="<td align=`"left`" style=`"border: 1px solid #ddd;background-color: #F66; padding-left: 6px; padding-right: 6px;`">";
        $nak="<td align=`"left`" style=`"border: 1px solid #ddd;background-color: #FFF; padding-left: 6px; padding-right: 6px; color: #CCC;`">"
        $warn="<td align=`"left`" style=`"border: 1px solid #ddd;background-color: #FF0; padding-left: 6px; padding-right: 6px;`">"
    }

    $htmldoc = ""
    if ($begindoc) {
        $htmldoc += "<html><head><STYLE TYPE=`"text/css`">$crlf"
        $htmldoc += "<!--$crlf"
        $htmldoc += "TABLE,TH,TD{font-family: Arial; font-size: 9pt;padding: 3px;border-collapse: collapse;}$crlf"
        $htmldoc += "BODY{font-family: Arial; font-size: 11pt; font-color: solid black;}$crlf"
        $htmldoc += "--->$crlf"
        $htmldoc += "</STYLE></head>$crlf<body>$crlf"
    }
    if ($headermsg) {
        $htmldoc += "<br><b>$headermsg</b><br>$crlf"
    }
    
    # Check if $objArray has any actual values, if not assume a null array was passed in
    # Check IsSettable because when $objArray is null, it implicitly becomes a [PSCustomObject[]] array which has inherited object properties where IsSettable is false that we need to ignore
    # Use PSObject.Properties because Get-Member sorts the object and we want the original order
    if ($objArray) {
        $objProps = $objArray[0].PSObject.Properties | Where-Object {$_.IsSettable} | Select-Object -ExpandProperty Name
        if ($objProps) {
            $htmldoc += "<br><table style=`"border: 1px solid #ddd;`">$crlf"
            $htmldoc += "<tr>"
    
            # Build the header part of the html table based on property names
            # Use psobject.properties because get-member sorts the object, we want the original order
            foreach($prop in $objProps) {
                $htmldoc += "$($tdhash["head"])$($prop)</b></td>$crlf"
            }
            $htmldoc += "</tr>$crlf"

            # Build the rows and columns of the html table
            foreach ($obj in ($objArray | Sort-Object Property)) {
                $htmldoc += "<tr>$crlf"
                foreach ($prop in $objProps) {
                    $value = $obj.$prop #use this instead of "-ExpandProperty $prop" because this one won't error if $prop doesn't exist
                    if (($value) -and (@($nok,$nak,$warn,$ok) -contains $value)) {
                        $htmldoc+="$($tdhash[$value])$($value)</td>$crlf" # Get <TD> text from the hash table for the static values (OK, NA, WARN, FAIL)
                    } else {
                        $htmldoc+="$($tdhash[$ok])$($value)</td>$crlf" # Anything not in the above categories will use the OK formatting
                    }
                }
                $htmldoc += "</tr>$crlf"
            }
            $htmldoc+="</table><br><br>$crlf"
        }
    }
    if ($footermsg) {
        $htmldoc += "<b>$footermsg<br>$crlf"
    }
    if ($enddoc) {
        $htmldoc += "</body></html>"
    }
    return $htmldoc
}