Senior Systems Engineer
Senior Systems Engineer

Reputation: 1141

Highlighting HTML body when specific string is exist partially working?

The below Powershell script is working partially in sending specific sets of Event ID related to DFS Replication as email.

However, I wanted to do:

  1. Display the result of the event logs as HTML email body, which is not displaying properly.
  2. Highlight in Red when the specific sets of Event ID exist in the HTML body. In Red: 2004, 2104, 2106, 2212, 4212, 5002, 5014

here's my attempt that ends up in failure:

            $mailArgs = @{
                SmtpServer  = 'mail.domain.com'
                From        = '[email protected]'
                To          = '[email protected]'
                Subject     = '{0} DFS report'
                Attachments = 'C:\DFSReports\DFS-Events_{0}.csv'
                BodyAsHtml  = $true
            }
            
$HTMLBeginning = @'
<!DOCTYPE html>
<html lang="en">
<head>
    <style>$HTMLBeginning = @'
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        body {{
            font-family: Arial;
        }}
        table {{
            width: 100%;
            border-collapse: collapse;
            border: 1px solid;
        }}
        th {{
            background-color: green;
            border: 1px solid;
            padding: 1px;
        }}
        td {{
            border: 1px solid;
            padding: 1px;
        }}
        .red_events {{
            color: red;
        }}
    </style>
    <title>DFS Replication Related Events since {0} hours ago</title>
</head>
<body>
    <table>
        <tr>
            <th>Time</th>
            <th>Event ID</th>
            <th>Message</th>
        </tr>
    <title>DFS Replication Related Events since {0} hours ago</title>
</head>
<body>
    <table>
        <tr>
            <th>Time</th>
            <th>Event ID</th>
            <th>Message</th>
        </tr>
'@

$HTMLEnd = @'
    </table>
</body>
</html>
'@

$HTMLBody = New-Object System.Collections.Generic.List[string]
$HTMLBody.Add($HTMLBeginning)

$EventIDs = 1006, 1008, 2004, 2104, 2212, 4202, 4206, 4208, 4212, 4304, 5002, 5012, 5014
$RedEvents = 2004, 2104, 2106, 2212, 4212, 5002, 5014
$Ago = -24

Get-DfsrMember | Select-Object -ExpandProperty ComputerName -Unique | Sort-Object | ForEach-Object {
    Write-Host "Processing $($_) ..."
    Try {
        $splat = $mailArgs.psobject.Copy()
        $splat['Attachments'] = $splat['Attachments'] -f $_
        $EventResults = Get-WinEvent -ComputerName $_ -FilterHashTable @{ LogName = 'DFS Replication'; StartTime = (Get-Date).AddHours($Ago); ID = $EventIDs } |
        Select-Object -Property TimeCreated, Id, Message |
        Sort-Object -Property TimeCreated 
        $EventResults | Export-Csv -NoTypeInformation -UseCulture -Path $splat['Attachments']
        $EventResults | foreach-Object {
            if ($_.ID -in $RedEvents) {
                $TableRow = @'
                <tr>
                    <td style="color:red;">{0}</td>
                    <td style="color:red;">{1}</td>
                    <td style="color:red;">{2}</td>
                    <td style="color:red;">{3}</td>
                </tr>
'@ -f $_.Time, $_.ID, $_.Message
            }
            else {
                $TableRow = @'
                <tr>
                    <td>{0}</td>
                    <td>{1}</td>
                    <td>{2}</td>
                </tr>
'@ -f $_.Time, $_.ID, $_.Message
            }
            $HTMLBody.Add($TableRow)
            Remove-variable -name TableRow
        }
        $HTMLBody.Add($HTMLEnd)
        $FinalBody = $HTMLBody -join '<br />' | Out-String
        $FinalBody = $FinalBody -f $Ago 
        $splat['Body'] = $FinalBody
    }
    Catch {
        Write-Host $splat['Attachments']
        Write-Error -ErrorRecord $_
        $splat['Body'] = "$($_) query failed:`r`n$($_.Exception.Message)"
        $splat.Remove('Attachments')
    }
    $splat['Subject'] = $splat['Subject'] -f $_
    Write-Host $splat['Attachments']
    Send-MailMessage @splat
}

The code is like in this screenshot:https://i.sstatic.net/WQhnD.png

The error is:

} : The given path's format is not supported.
    + CategoryInfo          : OpenError: (:) [Write-Error], NotSupportedException
    + FullyQualifiedErrorId : FileOpenFailure

Upvotes: 0

Views: 102

Answers (1)

Theo
Theo

Reputation: 61178

In order not to flood SO with any more comments, this is what I would try.

We've already tackeld some problems in the code:

  • if you want to build the HTML in a List object, either initialize it with New-Object System.Collections.Generic.List[string] or (faster) with [System.Collections.Generic.List[string]]::new()

    DONE

  • Using Here-Strings as templates is a good idea. Bear in ind that the closing '@ must/can not be preceeded by any whitespaces in the code.

    DONE

  • Using the -f Format operator to replace placeholders in a string needs extra attention when this string also contains curly brackets, not having to do with the placeholder itself. You can make this work if you double all { and } characters in the string except for the placeholder {0}

    DONE

  • Not every email client is capable of displaying font colors when used with CSS class names. To make sure the text is displayed in red, add the style inline, so <td style="color:red;">{0}</td> instead of <td class="red_event">{0}</td>

    DONE

What then still causes the error you are getting?

I did a re-write of your code and while doing so, I found that the $HTMLBeginning here-string was incorrect. (doubled lines like <!DOCTYPE html>).
I cannot test this in full, but please let me know if below code does what you intend to do.

$attachmentPath = 'C:\DFSReports'

# if the path does not yet exist, create it
if (!(Test-Path -Path $attachmentPath)) {
    $null = New-Item -Path $attachmentPath -ItemType Directory
}

# set up a Hashtable for splatting to Send-MailMessage
$mailArgs = @{
    SmtpServer  = 'mail.domain.com'
    From        = '[email protected]'
    To          = '[email protected]'
    Subject     = '{0} DFS report'
    Attachments = Join-Path -Path $attachmentPath -ChildPath 'DFS-Events_{0}.csv'
    BodyAsHtml  = $true
}

# template Here-Strings to build the HTML body       
$HTMLBeginning = @'
<!DOCTYPE html>
<html lang="en">
<head>
    <title>DFS Replication Related Events since ##HOURSAGO## hours ago</title>
    <style>
        body {
            font-family: Arial;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            border: 1px solid;
        }
        th {
            background-color: green;
            border: 1px solid;
            padding: 1px;
        }
        td {
            border: 1px solid;
            padding: 1px;
        }
    </style>
</head>
<body>
    <table>
        <tr>
            <th>Time</th>
            <th>Event ID</th>
            <th>Message</th>
        </tr>
'@

$HTMLEnd = @'
    </table>
</body>
</html>
'@

$TableRow = @'
                <tr>
                    <td{0}>{1}</td>
                    <td{0}>{2}</td>
                    <td{0}>{3}</td>
                </tr>
'@

# initialize a List object for strings
$HTMLBody = [System.Collections.Generic.List[string]]::new()
$EventIDs  = 1006, 1008, 2004, 2104, 2212, 4202, 4206, 4208, 4212, 4304, 5002, 5012, 5014
$RedEvents = 2004, 2104, 2106, 2212, 4212, 5002, 5014
$Ago = -24
$EventFilter = @{ LogName = 'DFS Replication'; StartTime = (Get-Date).AddHours($Ago); ID = $EventIDs }

Get-DfsrMember | Select-Object -ExpandProperty ComputerName -Unique | Sort-Object | ForEach-Object {
    # capture the computername in case we enter the catch block
    $computer = $_
    Write-Host "Processing $computer ..."
    # fill in details for the email parameters
    $splat = $mailArgs.psobject.Copy()
    $splat['Attachments'] = $splat['Attachments'] -f $computer
    $splat['Subject']     = $splat['Subject'] -f $computer

    # empty the List and start building the html for the next computer
    $HTMLBody.Clear()
    $HTMLBody.Add(($HTMLBeginning -replace '##HOURSAGO##', $Ago))

    Try {
        $EventResults = Get-WinEvent -ComputerName $_ -FilterHashTable $EventFilter |
                        Select-Object -Property TimeCreated, Id, Message |
                        Sort-Object -Property TimeCreated
        Write-Host "Outputting attachment file $($splat['Attachments'])"
        $EventResults | Export-Csv -Path $($splat['Attachments']) -UseCulture -NoTypeInformation
        $EventResults | ForEach-Object {
            $color = if ($_.Id -in $RedEvents) { ' style="color:red;"' } else { '' }
            $row   = $TableRow -f $color, $_.TimeCreated, $_.Id, $_.Message
            $HTMLBody.Add($row)
        }
        $HTMLBody.Add($HTMLEnd)
        # join the array parts of the HTMLBody with NewLines and set that as the Body parameter
        # P.S. I was wrong earlier, this needs "`r`n", not '<br />'
        $splat['Body'] = $HTMLBody.ToArray() -join "`r`n"
    }
    Catch {
        Write-Host $splat['Attachments']
        Write-Error $_.Exception.Message
        $splat['Body'] = "$computer query failed:<br />$($_.Exception.Message)"
        $splat.Remove('Attachments')
    }
    Write-Host $splat['Attachments']
    Send-MailMessage @splat
}

Upvotes: 1

Related Questions