While I've made plenty of changes and added a couple of new features, I want to focus on Tables again. I know it was already a focus for me in earlier releases, but I was missing a crucial element. Conditional formatting and styling of content inline. While Conditional Formatting was already part of previous versions, it was based on JavaScript. That means conditional formatting happens while you open up your browser. Thanks to this there's almost no time spent on verification whether something matches condition or not while it's being generated in PowerShell. The drawback is it doesn't work when JavaScript is disabled, which is something widespread for emails. This release adds the ability to style content based on column and rows or use conditional formatting to apply style but make it permanent in HTML. Confusing? Let's take a look at a reasonably simple table of Domain Controllers in my test Active Directory Forest.
Creating this table in HTML is not trivial if you would like to do it manually. There are multiple things in play here. First of all, we have headers that span across two rows. We have headers that span across multiple columns. We also have green, yellow, and red colors in different parts of content to make data in them more accessible to spot on first look. While manually it would be hard to create with PSWriteHTML (and for that matter in Emailimo or Dashimo) it's quite easy if you understand the concepts. Since Table above is a screenshot from Email, I will be using Emailimo to describe features you see above.
$DomainControllers = Get-WinADDomainControllers -TestAvailability
EmailTable -DataTable $DomainControllers {
EmailTableHeader -Names 'IPV4Address', 'IPV6Address' -Title 'Ip Addresses' -Alignment center -Color White -BackGroundColor Gray
EmailTableHeader -Names 'SchemaMaster', 'PDCEmulator', 'RIDMaster', 'DomainNamingMasterMaster', 'InfrastructureMaster' -Title 'Roles' -Alignment center -Color White -BackGroundColor Gray
EmailTableHeader -Names 'LdapPort', 'SslPort' -Title 'Ports' -Alignment center -Color White -BackGroundColor Gray
EmailTableCondition -ComparisonType 'string' -Name 'SchemaMaster' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'PDCEmulator' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'RIDMaster' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'IsReadOnly' -Operator eq -Value 'True' -BackgroundColor Yellow -Color Black -Inline
EmailTableCondition -ComparisonType 'string' -Name 'Pingable' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'Pingable' -Operator ne -Value 'True' -BackgroundColor Red -Color White -Inline
} -HideFooter
In code above, you see four functions: Get-WinADDomainControllers, which is getting me all my Domain Controllers within a forest (not part of today's blog). Then as you can see, I'm using three functions from Emailimo module – EmailTable, EmailTableHeader, and EmailTableCondition. First, you create a table with EmailTable; you open bracket to tell Emailimo that EmailTableHeader and EmailTableConditions function will apply to that table and nothing else and then you define their action. As you can see above, I've used three EmailTableHeader functions. I wanted to add a description and somehow connect all FSMO roles under one header called Roles. So I defined all Column Names and gave it a Title.
EmailTableHeader -Names 'SchemaMaster', 'PDCEmulator', 'RIDMaster', 'DomainNamingMasterMaster', 'InfrastructureMaster' -Title 'Roles' -Alignment center -Color White -BackGroundColor Gray
In the next case, I've two columns called LdapPort and SSLPort that I wanted to be visible under one name Ports. With Emailimo it's as simple as one command
Of course, as you may have already noticed, I also gave it some BackgroundColor some Color for text and some alignment. But there are other styling options available. But you already knew that from my earlier blog, right? But here's the critical bit EmailTableCondition. In this case, I've defined five conditions. I wanted to color code all Domain Controllers where the FSMO roles are. I also wanted to color code computers which are pingable and mark those which aren't with red color. This was already covered in my blog post about Dashimo. What wasn't included is the switch Inline. That little switch is critical if you want the styling to work with JavaScript disabled (hence why I show this in Emailimo).
EmailTableCondition -ComparisonType 'string' -Name 'SchemaMaster' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'PDCEmulator' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'RIDMaster' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'IsReadOnly' -Operator eq -Value 'True' -BackgroundColor Yellow -Color Black -Inline
EmailTableCondition -ComparisonType 'string' -Name 'Pingable' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'Pingable' -Operator ne -Value 'True' -BackgroundColor Red -Color White -Inline
How does it look in HTML?
<table id="DT-wmronOCJ" class="display compact">
<thead>
<tr>
<th rowspan="2">Domain</th>
<th rowspan="2">HostName</th>
<th rowspan="2">Forest</th>
<th style="color:#ffffff;background-color:#808080;text-align:center" colspan="2">Ip
Addresses</th>
<th rowspan="2">IsGlobalCatalog</th>
<th rowspan="2">IsReadOnly</th>
<th rowspan="2">Site</th>
<th style="color:#ffffff;background-color:#808080;text-align:center" colspan="5">Roles</th>
<th style="color:#ffffff;background-color:#808080;text-align:center" colspan="2">Ports</th>
<th rowspan="2">Comment</th>
<th rowspan="2">Pingable</th>
</tr>
<tr>
<th>IPV4Address</th>
<th>IPV6Address</th>
<th>SchemaMaster</th>
<th>DomainNamingMasterMaster</th>
<th>PDCEmulator</th>
<th>RIDMaster</th>
<th>InfrastructureMaster</th>
<th>LdapPort</th>
<th>SslPort</th>
</tr>
</thead>
<tbody>
<tr>
<td>ad.evotec.xyz</td>
<td>AD2.ad.evotec.xyz</td>
<td>ad.evotec.xyz</td>
<td>192.168.240.192</td>
<td></td>
<td>True</td>
<td>False</td>
<td>KATOWICE-1</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>389</td>
<td>636</td>
<td></td>
<td style="color:#ffffff;background-color:#008000">True</td>
</tr>
<tr>
<td>ad.evotec.xyz</td>
<td>AD1.ad.evotec.xyz</td>
<td>ad.evotec.xyz</td>
<td>192.168.240.189</td>
<td></td>
<td>True</td>
<td>False</td>
<td>KATOWICE-1</td>
<td style="color:#ffffff;background-color:#008000">True</td>
<td>True</td>
<td style="color:#ffffff;background-color:#008000">True</td>
<td style="color:#ffffff;background-color:#008000">True</td>
<td>True</td>
<td>389</td>
<td>636</td>
<td></td>
<td style="color:#ffffff;background-color:#008000">True</td>
</tr>
<tr>
<td>ad.evotec.xyz</td>
<td>AD3.ad.evotec.xyz</td>
<td>ad.evotec.xyz</td>
<td>192.168.240.236</td>
<td></td>
<td>True</td>
<td>False</td>
<td>KATOWICE-2</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>389</td>
<td>636</td>
<td></td>
<td style="color:#ffffff;background-color:#008000">True</td>
</tr>
<tr>
<td>ad.evotec.pl</td>
<td>ADPreview2019.ad.evotec.pl</td>
<td>ad.evotec.xyz</td>
<td>192.168.240.201</td>
<td></td>
<td>True</td>
<td>False</td>
<td>KATOWICE-2</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>389</td>
<td>636</td>
<td></td>
<td style="color:#ffffff;background-color:#008000">True</td>
</tr>
<tr>
<td>ad.evotec.pl</td>
<td>DC1.ad.evotec.pl</td>
<td>ad.evotec.xyz</td>
<td>192.168.240.238</td>
<td></td>
<td>True</td>
<td>False</td>
<td>KATOWICE-1</td>
<td>False</td>
<td>False</td>
<td style="color:#ffffff;background-color:#008000">True</td>
<td style="color:#ffffff;background-color:#008000">True</td>
<td>True</td>
<td>389</td>
<td>636</td>
<td></td>
<td style="color:#ffffff;background-color:#008000">True</td>
</tr>
<tr>
<td>ad.evotec.pl</td>
<td>ADRODC.ad.evotec.pl</td>
<td>ad.evotec.xyz</td>
<td>192.168.240.207</td>
<td></td>
<td>True</td>
<td style="color:#000000;background-color:#ffff00">True</td>
<td>GLIWICE</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>False</td>
<td>389</td>
<td>636</td>
<td></td>
<td style="color:#ffffff;background-color:#ff0000">False</td>
</tr>
</tbody>
</table>
What this Inline switch did is apply style for each Table Cell separately as required. It does this for headers with row spanning and column spanning and does this for styling. For this feature to work, it means that I have to rebuild table on the fly in PowerShell rather than relying on JavaScript at runtime. That means you may see some speed difference when using EmailTableCondition (Emailimo)/TableCondition (Dashimo)/New-HTMLTableCondition (PSWriteHTML) with and without Inline switch. Full code to design and send email is as below.
Import-Module Emailimo -Force
$DomainControllers = Get-WinADDomainControllers -TestAvailability
Email {
EmailHeader {
EmailFrom -Address 'reminder@evotec.pl'
EmailTo -Addresses "kontakt@evotec.pl"
EmailServer -Server 'smtp.office365.com' -UserName 'rpassword' -Password 'C:\Support\Important\Password-Evotec-Reminder.txt' -PasswordAsSecure -PasswordFromFile
EmailOptions -Priority High -DeliveryNotifications Never
EmailSubject -Subject 'This is a test email'
}
EmailBody -FontFamily 'Calibri' -Size 15 {
EmailText -Text "Domain Controllers ", 'Status' -Color Blue, None
EmailTable -DataTable $DomainControllers {
EmailTableHeader -Names 'IPV4Address', 'IPV6Address' -Title 'Ip Addresses' -Alignment center -Color White -BackGroundColor Gray
EmailTableHeader -Names 'SchemaMaster', 'PDCEmulator', 'RIDMaster', 'DomainNamingMasterMaster', 'InfrastructureMaster' -Title 'Roles' -Alignment center -Color White -BackGroundColor Gray
EmailTableHeader -Names 'LdapPort', 'SslPort' -Title 'Ports' -Alignment center -Color White -BackGroundColor Gray
EmailTableCondition -ComparisonType 'string' -Name 'SchemaMaster' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'PDCEmulator' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'RIDMaster' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'IsReadOnly' -Operator eq -Value 'True' -BackgroundColor Yellow -Color Black -Inline
EmailTableCondition -ComparisonType 'string' -Name 'Pingable' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline
EmailTableCondition -ComparisonType 'string' -Name 'Pingable' -Operator ne -Value 'True' -BackgroundColor Red -Color White -Inline
} -HideFooter
EmailText -LineBreak
EmailTextBox {
'Kind regards,'
'Evotec IT'
}
}
}
In just 35 lines, I was able to produce a nicely formatted email with readable code. Something that wasn't possible before.