HTML E-Mail Report Generator
Overview
I created this series of functions to allow you to conveniently take one or more PowerShell object and have it turn into an email report. For example, if you had a list of computers you have come out of a PowerShell script you wrote, these functions could help you turn them into an email with the same data without much effort.
Create an Email with A Simple Array of Data: EASY
Import-Module \\scriptserver\scripts\Get-DGMEmailReport\Get-DGMEmailReport.psm1 -Force
Import-Module \\pathtoscriptfile\Get-DGMEmailReport.psm1 -Force
$myarray = (Get-ADComputer -properties Description -filter {description -like "*sql*"} | Select Name,Description)
#Single Array Email Report
Get-DGMEmailReport `
-Array $myarray `
-ArrayTitle "SQL Servers" `
-ArrayTitleColor "Blue" `
-ArrayMessage "These are all the SQL Servers" `
-ReportTitle "SQL Report 2017" `
-from "SQLReports@emailaddress.com" `
-To "c-dmaiolo@emailaddress.com" `
-subject "SQL Test Report"
Create an Email With Multiple Arrays of Data: MODERATE DIFFICULTY
Import-Module \\pathtoscriptfile\Get-DGMEmailReport.psm1 -Force
#Create Some Arrays Of Data To Display in Report. You can create as many as you want.
$OutputArrays = @()
#Array1
$output = [PSCustomObject] @{
'Message' = "These are deployments older than 90 days, with lower than an 80% success rate where 100 or more computers were targeted.";
'Title' = "SCCM Problem Deployments";
'Color' = "Red";
'Array' = Get-DGMSCCMProblemDeploymentsArray -Days 90 -PercentSuccessThreshold .8 -NumberOfTargetedThreshold 100;
}
$OutputArrays+=$output
#Array2
$output = [PSCustomObject] @{
'Message' = "These are all deployments that were deployed over a year ago.";
'Title' = "SCCM Deployments Greater Than 1 Year Old";
'Color' = "Green";
'Array' = Get-DGMSCCMProblemDeploymentsArray -Days 365 -PercentSuccessThreshold 1 -NumberOfTargetedThreshold 0;
}
$OutputArrays+=$output
#Array3
$output = [PSCustomObject] @{
'Message' = "These are all our SQL Servers.";
'Title' = "SQL Servers";
'Array' = Get-ADComputer -properties Description -filter {description -like "*sql*"} | Select Name,Description
}
$OutputArrays+=$output
#Multiple Arrays Email Report
Get-DGMEmailReport `
-Arrays $OutputArrays `
-ReportTitle "SQL Report 2017" `
-from "SQLReports@emailaddress.com" `
-To "c-dmaiolo@emailaddress.com" `
-subject "SQL Test Report"
Sample Single Array Output
Sample Multiple Array Output Email
PowerShell Functions
<#
.SYNOPSIS
Generates an Email Report
.NOTES
Version: 1.0
Author: David Maiolo
Creation Date: 2018-01-02
Purpose/Change: Initial script development
#>
#---------------------------------------------------------[Initialisations]--------------------------------------------------------
Import-Module \\scriptserver\scripts\DMGSCCM\New-DMGCMTraceLog\New-DMGCMTraceLog.psm1 -Force
function New-DMGCombinedHTMLTable{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
$DMGCombinedHTMLTable
)
$CombinedHTML = ""
foreach ($object in $DMGCombinedHTMLTable){
$CombinedHTML+= ""
if ($object.Title){
if ($object.Color){
$CombinedHTML+= ""
#$CombinedHTML+= ""+($object.Title)+"
"
$CombinedHTML+= ""+($object.Title)+"
"
}else{
$CombinedHTML+= ""+($object.Title)+"
"
}
}
if($object.Message){
$CombinedHTML+= "➥ "+($object.Message)+"
"
}
if ($object.Array){
$CombinedHTML+= $object.Array | ConvertTo-Html
}
$CombinedHTML+= ""
}
return $CombinedHTML
}
function New-DMGHTMLTable{
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
$Array,
[Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$true)]
$Title,
[Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$true)]
$Message,
[Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateSet("Black","White","Red","Lime","Blue","Yellow","Cyan","Magenta","Silver","Gray","Maroon","Olive","Green","Purple","Teal","Navy")]
$Color
)
$HTML = ""
$HTML+= ""
if([bool]($MyInvocation.BoundParameters.Keys -match 'Title')){
if([bool]($MyInvocation.BoundParameters.Keys -match 'Color')){
$HTML+= ""
}
$HTML+= ""+($Title)+"
"
}
if([bool]($MyInvocation.BoundParameters.Keys -match 'Message')){
$HTML+= "➥ "+($Message)+"
"
}
if([bool]($MyInvocation.BoundParameters.Keys -match 'Array')){
$HTML+= $Array | ConvertTo-Html
}
$HTML+= ""
return $HTML
}
function New-DMGEmailReport{
param(
[Parameter(Position=0,Mandatory=$false,ValueFromPipeline=$true,ParameterSetName='Multiple Arrays')]
$Arrays,
[Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$true,ParameterSetName='Single Array')]
$Array,
[Parameter(Position=2,Mandatory=$false,ValueFromPipeline=$true,ParameterSetName='Single Array')]
$ArrayMessage,
[Parameter(Position=3,Mandatory=$false,ValueFromPipeline=$true,ParameterSetName='Single Array')]
$ArrayTitle,
[Parameter(Position=9,Mandatory=$false,ValueFromPipeline=$true,ParameterSetName='Single Array')]
[ValidateSet("Red","Yellow","Green","Black")]
$ArrayTableColor = "Black",
[Parameter(Position=4,Mandatory=$true,ValueFromPipeline=$true)]
$From,
[Parameter(Position=5,Mandatory=$true,ValueFromPipeline=$true)]
$To,
[Parameter(Position=6,Mandatory=$true,ValueFromPipeline=$true)]
$Subject,
[Parameter(Position=7,Mandatory=$true,ValueFromPipeline=$true)]
$ReportTitle,
[Parameter(Position=8,Mandatory=$false,ValueFromPipeline=$true)]
$SmtpServer = "mail.emailaddress.com",
[Parameter(Position=9,Mandatory=$false,ValueFromPipeline=$true)]
[Switch]$AttatchResults,
[Parameter(Position=10,Mandatory=$false,ValueFromPipeline=$true)]
[Switch]$AttatchSource
)
#Set Logging Varibales
$invocation = (Get-Variable MyInvocation -Scope 1).Value
$ScriptDirectory = Split-Path $invocation.MyCommand.Path
$ScriptName = ($MyInvocation.MyCommand.Name)+".psm1"
$LogName = ($MyInvocation.MyCommand.Name)+".log"
$LogFile = Join-Path $ScriptDirectory $LogName
$ScriptFile = Join-Path $ScriptDirectory $ScriptName
$ReportDate = Get-Date
$WrapperScriptPath = $MyInvocation.PSCommandPath
#Map a Drive to UNC Path
New-PSDrive -Name UNCPath -PSProvider FileSystem -Root $ScriptDirectory
#Log Start of Function
New-DMGCMTraceLog -message ("Starting Logging for $ScriptName") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
#Create Attachments Directory If It Doesn't Exist
if (!(Test-Path "UNCPath:\Attachments\")){
New-DMGCMTraceLog -message ("UNCPath:\Attachments\ Not Found. Created.") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
New-Item "UNCPath:\Attachments\" -ItemType Directory
}
if([bool]($MyInvocation.BoundParameters.Keys -contains 'Arrays')){
$HTMLTable = New-DMGCombinedHTMLTable -DMGCombinedHTMLTable $Arrays
#Log
New-DMGCMTraceLog -message ("An Array of Arrays Was Passed") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
if([bool]($MyInvocation.BoundParameters.Keys -contains 'AttatchResults')){
$attachment = @()
foreach ($object in $Arrays){
$AttachmentName = (Remove-DMGInvalidFileNameChars($object.Title))+".csv"
$AttachmentFile = Join-Path UNCPath:\Attachments\ $AttachmentName
$object.Array | export-csv -path $AttachmentFile -notypeinformation
$attachment += $AttachmentFile
}
New-DMGCMTraceLog -message ("Attatching multiple files: $attachment") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}
}elseif([bool]($MyInvocation.BoundParameters.Keys -contains 'Array')){
$HTMLTable = New-DMGHTMLTable -Array $Array -Message $ArrayMessage -Title $ArrayTitle -Color $ArrayTableColor
#Log
New-DMGCMTraceLog -message ("An Single Array Was Passed") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
if([bool]($MyInvocation.BoundParameters.Keys -contains 'AttatchResults')){
$attachment = @()
$AttachmentName = (Remove-DMGInvalidFileNameChars($ArrayTitle))+".csv"
$AttachmentFile = Join-Path UNCPath:\Attachments\ $AttachmentName
$Array | export-csv -path $AttachmentFile -notypeinformation
$attachment += $AttachmentFile
}
New-DMGCMTraceLog -message ("Attatching the single file: $Attachment") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}
#Construct HTML for Email
$HTMLEmailMessage = @"
CodeNarc Report: Sample Project
$Subject
$HTMLTable
Path to script: $ScriptFile
Path to log file: $LogFile
Path to script that called $ScriptName`: $WrapperScriptPath
"@
if([bool]($MyInvocation.BoundParameters.Keys -contains 'AttatchSource')){
if ($attachment -eq $null){$attachment = @()}
$AttachmentName = (Remove-DMGInvalidFileNameChars($ReportTitle))+"_html_source.txt"
$AttachmentFile = Join-Path UNCPath:\ $AttachmentName
$HTMLEmailMessage | Out-File -FilePath $AttachmentFile
$attachment += $AttachmentFile
}
#Send The Email
if ($Attachment){
Send-MailMessage -From $from -To $To -Subject $subject -SmtpServer $smtpServer -BodyAsHtml -Body $HTMLEmailMessage -Attachments $Attachment
#Log Sent Email
New-DMGCMTraceLog -message ("Email Sent`: Subject`:$subject To`:$To From`:$From Attatchments`:$attachment") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}else{
Send-MailMessage -From $from -To $To -Subject $subject -SmtpServer $smtpServer -BodyAsHtml -Body $HTMLEmailMessage
#Log Sent Email
New-DMGCMTraceLog -message ("Email Sent`: Subject`:$subject To`:$To From`:$From") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}
#Remove Attachments
if (Test-Path "UNCPath:\Attachments\"){
Remove-Item "UNCPath:\Attachments\" -Recurse
New-DMGCMTraceLog -message ("UNCPath:\Attachments\ Found. Removing files within.") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}
#Log End Of Function
New-DMGCMTraceLog -message ("End Logging for $ScriptName") -component "Main()" -type 1 -ScriptName $ScriptName -LogFile $LogFile -ScriptFile $ScriptFile
}
function Get-DMGColorHexValue{
param(
[Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateSet("Black","White","Red","Lime","Blue","Yellow","Cyan","Magenta","Silver","Gray","Maroon","Olive","Green","Purple","Teal","Navy")]
$Color
)
switch ($Color)
{
"Black" {"#000000"}
"White" {"#FFFFFF"}
"Red" {"#FF0000"}
"Lime" {"#00FF00"}
"Blue" {"#0000FF"}
"Yellow" {"#FFFF00"}
"Cyan" {"#00FFFF"}
"Magenta" {"#FF00FF"}
"Silver" {"#C0C0C0"}
"Gray" {"#808080"}
"Maroon" {"#800000"}
"Olive" {"#808000"}
"Green" {"#008000"}
"Purple" {"#800080"}
"Teal" {"#008080"}
"Navy" {"#000080"}
default {"#DDDDFF"}
}
}
Function Remove-DMGInvalidFileNameChars {
param(
[Parameter(Mandatory=$true,
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String]$String
)
$pattern = '[^a-zA-Z]'
$CleanString = $String -replace $pattern
return $CleanString
}
PowerShell Code: The Logging Functions
Import this module to enable the logging functions.
function New-DGMCMTraceLog{
param (
[Parameter(Mandatory=$true)]
$ScriptName,
[Parameter(Mandatory=$true)]
$LogFile,
[Parameter(Mandatory=$true)]
$ScriptFile,
[Parameter(Mandatory=$true)]
$message,
[Parameter(Mandatory=$true)]
$component,
[Parameter(Mandatory=$true)]
$type )
$VerboseLogging = "true"
[bool]$Verbose = [System.Convert]::ToBoolean($VerboseLogging)
$MaxLogSizeInKB = 10240
$ScriptStatus = 'Success'
switch ($type)
{
1 { $type = "Info" }
2 { $type = "Warning" }
3 { $type = "Error" }
4 { $type = "Verbose" }
}
if (($type -eq "Verbose") -and ($Verbose))
{
$toLog = "{0} `$$<{1}><{2} {3}>" -f ($type + ":" + $message), ($ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
$toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $LogFile)
Write-Host $message
}
elseif ($type -ne "Verbose")
{
$toLog = "{0} `$$<{1}><{2} {3}>" -f ($type + ":" + $message), ($ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
$toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $LogFile)
if ($type -eq 'Info') { Write-Host $message }
if ($type -eq 'Warning') { Write-Host $message -ForegroundColor Yellow}
if ($type -eq 'Error') { Write-Host $message -ForegroundColor Red}
}
if (($type -eq 'Warning') -and ($ScriptStatus -ne 'Error')) { $ScriptStatus = $type }
if ($type -eq 'Error') { $ScriptStatus = $type }
if ((Get-Item $LogFile -ErrorAction SilentlyContinue).Length/1KB -gt $MaxLogSizeInKB)
{
$log = $LogFile
Remove-Item ($log.Replace(".log", ".lo_"))
Rename-Item $LogFile ($log.Replace(".log", ".lo_")) -Force
}
}
Leave a Reply
Want to join the discussion?Feel free to contribute!