Creating Colorful Emails with PowerShell

color-aspect-hero
In the comments on a recent article about creating a color coded report of domain controller service statuses, there was a question in the comments about sending the same type of information, presumably colorized, in an email message. PowerShell makes it very easy to send email with the Send-MailMessage cmdlet. You can even send HTML messages, which is the key to answering this question.

As in the previous articles, I need to get the service information from my domain controllers. Of course, this could be other data that you want to report.

$dcs = "chi-dc01","chi-dc02","chi-dc04"
$svcs = "adws","dns","kdc","netlogon"
$data = Get-Service -name $svcs -ComputerName $dcs

I’ll also define a title for the report.

$ReportTitle = "Domain Controller Services"

Now for the first tricky part. When we create HTML documents in PowerShell, typically with ConvertTo-HTML, style is applied separately, usually through a CSS file. But you can also embed style information in the head of an HTML document. I’ll create a here-string with an embedded style sheet.

$head = @"
<Title>$ReportTitle</Title>
<style>
body { background-color:#FFFFFF;
font-family:Tahoma;
font-size:12pt; }
td, th { border:1px solid black;
border-collapse:collapse; }
th { color:white;
background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px }
table { width:95%;margin-left:5px; margin-bottom:20px;}
.stopped {color: Red }
.running {color: Green }
.pending {color: #DF01D7 }
.paused {color: #FF8000 }
.other {color: Black }
</style>
<br>
<H1>$ReportTitle</H1>
"@

The relevant parts for dynamically colorizing our output is in lines 13 to 17. These settings will be used to define a new style class. The settings inside the curly brackets will be applied for each match. All I’m doing is setting the font color. I’m using a mix of color names like red and HTML color codes. The better practice would probably be to use color codes for everything. Now we’re ready to start generating some HTML.
I only need a few properties, so I’ll select them and pipe to Convertto-Html. I don’t want a complete document yet, because I’m going to have to adjust it first to account for the different service statues. I have found the easiest solution is to create an HTML fragment, but treat it as an XML document.

[xml]$html = $data |
Select @{Name="DomainController";Expression={$_.MachineName.ToUpper()}},Name,Displayname,Status |
ConvertTo-Html -Fragment


Why? Because I can now “walk” the document.

HTML as an XML document
HTML as an XML document (Image Credit: Jeff HIcks)

It will be much easier to reference specific rows and columns. I’ll skip the first row since it is the table header.

1..($html.table.tr.count-1) | foreach {
#enumerate each TD
$html.table.tr[$_]
}

Enumerating the table
Enumerating the table (Image Credit: Jeff Hicks)

What I’m really seeing, remember this is an XML document, is a collection of child nodes.

1..($html.table.tr.count-1) | foreach {
#enumerate each TD
$td = $html.table.tr[$_]
$td.childnodes
}

Expanding the table rows
Expanding the table rows (Image Credit: Jeff Hicks)

Because I know the Status column is the last row, I can select just that node.

1..($html.table.tr.count-1) | foreach {
#enumerate each TD
$td = $html.table.tr[$_]
$td.childnodes.item(3)
}

Listing text values
Listing text values (Image Credit: Jeff HIcks)

My intention is to add a style class to this column based on the text value.

1..($html.table.tr.count-1) | foreach {
#enumerate each TD
$td = $html.table.tr[$_]
#create a new class attribute
$class = $html.CreateAttribute("class")
#set the class value based on the item value
Switch ($td.childnodes.item(3).'#text') {
"Running" { $class.value = "running"}
"Stopped" { $class.value = "stopped"}
"Pending" { $class.value = "pending"}
"paused" { $class.value = "paused"}
Default { $class.value = "other"}
}
#append the class
$td.childnodes.item(3).attributes.append($class) | Out-Null
}

If I look again, I’ll now see the class attribute.

The new class attribute
The new class attribute (Image Credit: Jeff Hicks)

At this point my HTML is complete, and I can see it as the InnerXML property of my XML document. I’ve highlighted a few of the changes.
The updated HTML code
The updated HTML code (Image Credit: Jeff Hicks)

All that remains now is to create the final HTML report. If I wanted to create a stand-alone report I can use ConvertTo-Html and send the results to a file.
ConvertTo-HTML -Head $head -Body $html.InnerXml -PostContent “<h6>Created $(Get-Date)</h6>” |
Out-File -filepath c:\work\DCSvcs.htm -Encoding asci
The end result is an HTML report that looks like this:
Sample HTML report
Sample HTML report (Image Credit: Jeff HIcks)

Or if I want to send this as an email message, I’ll convert to HTML and save the results to a variable.

[string]$body = ConvertTo-HTML -Head $head -Body $html.InnerXml -PostContent "<h6>Created $(Get-Date)</h6>"

The body needs to be a string, which is why I am explicitly telling PowerShell to treat it as a string. I can define a hashtable of parameter values for Send-MailMessage.

$mailParams=@{
To = "[email protected]"
From = "[email protected]"
Subject = "Domain Controller Status Report"
SMTPServer = "smtp.mycompany.com"
Body = $Body
BodyAsHTML = $True
Port = 587
UseSSL = $True
Credential = $mailCred
}

You will have different needs depending on your mail server. I splat the parameters:

Send-MailMessage @mailparams

And the mail is sent.

Sample HTML email
Sample HTML email (Image Credit: Jeff Hicks)


So the long answer to the original question is that you can send a colorized email, but you’ll need an HTML document that has been dynamically modified with some styling. I use many of these techniques for a lot of my HTML reporting needs. If I lost you anywhere in the process, please feel free to post a question in the comments.