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!