Reputation: 1141
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:
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
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