E-mail campaign for our internal iPhone 16e rollout, I thought it would be straightforward. How wrong I was. What started as a simple HTML email became a deep dive into the quirks of Outlook rendering, PowerShell SMTP limitations, Active Directory integration - simple idea, complex outcomes!
Visual Results
This is the email that was sent to people letting them know about a new phone, however lets look later in the post about the "Click here to upgrade"
The Challenge: More Than Just an Email
Our IT department needed to announce the availability of iPhone 16e devices to eligible employees. The goals were clear, which ironically also looked like phishing email requirements (maybe this could be weaponised this later on)
- Create urgency around limited availability
- Drive clicks to the upgrade portal
- Ensure 100% compatibility across email clients
- Target different departments with appropriate messaging
- Automate the process using existing Active Directory data
What I didn't anticipate was how this would become a masterclass in email client compatibility, organizational data integration, and corporate psychology or everyone wanting the next "corporate" standard phone.
Initial Design: The Web vs. Email Reality Check
I started with what I thought was a bulletproof approach - a modern HTML email template with CSS styling:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border-radius: 12px;
}
.upgrade-button {
background-color: #0071e3;
color: white;
padding: 16px 24px;
border-radius: 8px;
}
</style>
</head>
This looked perfect in web browsers and Outlook Web Access. Then I tested it in Outlook desktop it went a little wrong, the button disappeared, rounded corners vanished, and checkmarks turned into blue lines, the problem was Outlook's awful rendering engine.
The Outlook Compatibility Nightmare
Problem 1: The Invisible Button
My first major issue was the upgrade button simply not appearing in Outlook desktop:
<!-- This DOESN'T work in Outlook -->
<a href="#" class="upgrade-button">CLICK HERE TO UPGRADE</a>
This only successfully worked when we used table-based buttons with inline styling:
<!-- This WORKS in Outlook -->
<table border="0" cellpadding="0" cellspacing="0" width="300" bgcolor="#0071e3">
<tr>
<td align="center" style="padding: 12px 30px;">
<a href="#" style="color: #ffffff; display: block; font-family: Arial,
sans-serif; font-size: 18px; font-weight: bold; text-decoration: none;">
CLICK HERE TO UPGRADE</a>
</td>
</tr>
</table>
Problem 2: The Blue Line Mystery
The benefit checkmarks were rendering as blue lines instead of proper checkmarks:
<!-- This created blue lines in Outlook -->
<div style="background-color: #0071e3; border-radius: 12px;">✓</div>
This again required nested table approach with explicit dimensions included:
<!-- This renders properly in Outlook -->
<table border="0" cellpadding="0" cellspacing="0" width="24" height="24" bgcolor="#0071e3">
<tr>
<td align="center" valign="middle" style="color: #ffffff; font-weight: bold;">✓</td>
</tr>
</table>
Problem 3: Image Embedding Issues
Initially, I tried base64 encoding for the iPhone image:
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABA..." />
While this worked in web clients, Outlook had rendering issues. The solution was using Content-ID references with PowerShell attachments:
<img src="cid:iPhoneImage" alt="iPhone 16e" style="display: block; width: 100%;" />
With the corresponding PowerShell code:
# Create attachment with Content-ID
$attachment = New-Object System.Net.Mail.Attachment("iphone-image.jpg")
$attachment.ContentId = "iPhoneImage"
$attachment.ContentDisposition.Inline = $true
$message.Attachments.Add($attachment)
Hyperlink : Click here to upgrade
This is where it may have got fun as this link could be official or part of an internal phishing test which makes if more spicy, especially if these emails are rolled out with the actual rollout of distribution of iPhones.
I did wonder if fear of missing out (FOMO) would cause people to click on the link without actually checking that link completely due to "wants" of a new phone, which means the moment the link for this post is set to, which means it does nothing as below:
<td align="center" style="padding: 12px 30px;">
<a href="#" style="color: #ffffff; display: block; font-family: Arial,
sans-serif; font-size: 18px; font-weight: bold; text-decoration: none;">
CLICK HERE TO UPGRADE</a>
<a href="
https://wwwsrv1.bear.local/iphone16upgrade"
style="color: #ffffff; display: block; font-family: Arial,
Then on the web server (IIS) the backend for processing we could create an IIS website that uses Windows Authentication with Negotiate as the provider as below:You will then end up with a IIS log entry like this where you can easily identify the user that has clicked on the email from the log, see below:
2025-06-23 16:06:29 <source_ip> GET /iPhone16e/ -
443 BEAR\lee 10.84.77.259 Mozilla/5.0+
(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/137.0.0.0
+Safari/537.36+Edg/137.0.0.0 https://wwwsrv1.bear.local/iphone16upgrade 200 0 0 17
2025-06-23 16:06:29 <source_ip> GET /iPhone16e/beacon.png -
443 BEAR\lee 10.245.161.136 Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)
+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/137.0.0.0+Safari/537.36+Edg/137.0.0.0
https://www.srv1.bear.local/iphone16upgrade 200 0 0 230
I then wondered if we could change the content of the e-mail for certain departments based on the OU value they have as the user account, so lets dig a little deeper into that outcome shall we, and that sounds fund to code, if you are interested in that then please read on.
Organizational Integration: Department-Based Dynamic Content
Once I had the technical issues resolved, I realized we needed a more sophisticated approach. Different departments should receive different messaging - IT and Sales teams needed an "eligible" message, while other departments needed more informational content.
Rather than manually managing department lists, I implemented dynamic content generation based on Active Directory organizational units (OUs). This allowed for:
- Version A (Priority Departments): "You're Now Eligible to Upgrade!" - Personal, urgent messaging
- Version B (General Departments): "iPhone 16e Now Available" - Informational, check-eligibility messaging
Active Directory Integration
The key breakthrough was extracting department information directly from Distinguished Names (DNs) in Active Directory:
function Get-DepartmentFromDN($distinguishedName) {
Write-Verbose "Processing DN: $distinguishedName"
# Split the DN into components
$dnComponents = $distinguishedName -split ","
# Find OU components (excluding the CN)
$ouComponents = $dnComponents | Where-Object { $_ -like "OU=*" } | ForEach-Object {
$_.Substring(3) # Remove "OU=" prefix
}
# Define department keywords to look for
$departmentKeywords = @{
"IT" = @("IT", "Information Technology", "Technology", "Technical")
"Sales" = @("Sales", "Business Development", "Commercial")
"Marketing" = @("Marketing", "Communications", "PR")
"Finance" = @("Finance", "Accounting", "Financial")
"HR" = @("HR", "Human Resources", "People", "Personnel")
"Management" = @("Management", "Executive", "Leadership", "Directors")
"Operations" = @("Operations", "Ops", "Operational")
}
# Check each OU component against department keywords
foreach ($ou in $ouComponents) {
foreach ($dept in $departmentKeywords.Keys) {
foreach ($keyword in $departmentKeywords[$dept]) {
if ($ou -like "*$keyword*") {
Write-Verbose "Matched '$ou' to department '$dept' via keyword
'$keyword'"
return $dept
}
}
}
}
# Fallback to first meaningful OU
$meaningfulOUs = $ouComponents | Where-Object {
$_ -notlike "*Users*" -and
$_ -notlike "*Computers*" -and
$_ -notlike "*Service*"
}
if ($meaningfulOUs.Count -gt 0) {
return $meaningfulOUs[0]
}
return "Unknown"
}
Dynamic Email Variant Selection
Based on the department extracted from AD, I implemented variant selection:
function Get-EmailVariant($department) {
# High-priority departments get the "eligible" message
$highPriorityDepts = @("IT", "Management", "Sales", "Finance")
if ($department -in $highPriorityDepts) {
return @{
Subject = "You're Now Eligible to Upgrade!"
Header = "YOU'RE NOW ELIGIBLE<br>TO UPGRADE!"
MainText = "you're now eligible to upgrade your work phone"
Urgency = "This upgrade program is available for a limited time.
Complete your request by June 15, 2025 to secure your new device."
ButtonText = "CLICK HERE TO UPGRADE"
}
} else {
return @{
Subject = "iPhone 16e Now Available"
Header = "iPHONE 16e<br>NOW AVAILABLE"
MainText = "the iPhone 16e is now available for eligible employees"
Urgency = "Contact IT to check your upgrade eligibility and availability."
ButtonText = "CHECK ELIGIBILITY"
}
}
}
Template with Dynamic Placeholders
The HTML template was enhanced to support dynamic content injection:
<!-- Header section with dynamic content -->
<tr>
<td align="center" style="padding: 30px 20px;">
<h1 style="font-size: 36px; font-weight: bold; margin: 0; color: #1d1d1f;
text-transform: uppercase;">
{{HEADER}}
</h1>
</td>
</tr>
<!-- Main content with personalized messaging -->
<tr>
<td style="padding: 20px;">
<img src="cid:iPhoneImage" alt="iPhone 16e" style="display: block; width: 100%;" />
<h2 style="font-size: 36px; font-weight: bold; margin: 0; color: #1d1d1f;">
iPhone 16e</h2>
<p style="font-size: 24px; margin: 5px 0 20px; color: #0071e3;">Now in Stock</p>
<p style="font-size: 16px; margin: 20px 0; line-height: 1.5;">
The iPhone 16e has recently come into stock and {{MAIN_TEXT}}.
</p>
<!-- Benefits section stays consistent -->
<p style="font-size: 16px; margin: 20px 0; line-height: 1.5;">
{{URGENCY}}
</p>
<!-- Dynamic button text -->
<table border="0" cellpadding="0" cellspacing="0" width="300" bgcolor="#0071e3">
<tr>
<td align="center" style="padding: 12px 30px;">
<a href="#" style="color: #ffffff; display: block; font-family:
Arial, sans-serif; font-size: 18px; font-weight: bold;
text-decoration: none;">
{{BUTTON_TEXT}}
</a>
</td>
</tr>
</table>
</td>
</tr>
Visuals from the Dynamic Content delivery
If you do go down the dynamic emails to certain departments there you can also pull some very nice reports from the IIS logs, first you will need the log file from IIS then from that
.\Analyse-iPhone16Basic.ps1 -LogFilePath u_ex220817.log
This will then give you the basic report that will track opens, beacons and URL clicks as below:
However if you want to go a little deeper in the analysis you can run this deatailed version:
.\Analyse-iPhone16Basic.ps1 -LogFilePath u_ex220817.log
This will break this down until more details statistics as below:
Here's my final PowerShell script however you will need to update the sections in bold which include the base HTML and PNG of the iPhone 16e, also do not forget to update the SMTP server details.
# Import required modules
Import-Module ActiveDirectory
# Email configuration
$smtpServer = "smtp.bear.local"
$smtpPort = 25
# Enhanced user processing with DN handling
function Process-ADUsers {
$adUsers = Get-ADUser -Filter {
Enabled -eq $true -and
EmailAddress -like "*@company.com"
} -Properties EmailAddress, DistinguishedName, GivenName, Surname, Department
Write-Host "Retrieved $($adUsers.Count) users from Active Directory"
# Process each user
$processedUsers = foreach ($user in $adUsers) {
$department = if ($user.Department) {
$user.Department
} else {
Get-DepartmentFromDN $user.DistinguishedName
}
[PSCustomObject]@{
Email = $user.EmailAddress
FirstName = $user.GivenName
LastName = $user.Surname
Department = $department
DistinguishedName = $user.DistinguishedName
FullName = "$($user.GivenName) $($user.Surname)"
}
}
return $processedUsers
}
# Main execution
$users = Process-ADUsers
# Group by department for reporting
$departmentGroups = $users | Group-Object Department | Sort-Object Name
Write-Host "`nDepartment Distribution:" -ForegroundColor Green
foreach ($group in $departmentGroups) {
Write-Host " $($group.Name): $($group.Count) users" -ForegroundColor Cyan
}
# Send personalized emails
foreach ($user in $users) {
if (-not $user.Email) {
Write-Warning "Skipping user $($user.FullName) - no email address"
continue
}
try {
# Get appropriate email variant
$variant = Get-EmailVariant $user.Department
# Load and customize template
$htmlTemplate = Get-Content ".\email-template.html" -Raw
$personalizedHtml = $htmlTemplate -replace "{{HEADER}}", $variant.Header
$personalizedHtml = $personalizedHtml -replace "{{MAIN_TEXT}}", $variant.MainText
$personalizedHtml = $personalizedHtml -replace "{{URGENCY}}", $variant.Urgency
$personalizedHtml = $personalizedHtml -replace "{{BUTTON_TEXT}}",
$variant.ButtonText
# Create email message
$message = New-Object System.Net.Mail.MailMessage
$message.From = "phone.requests@bythepowerofgreyskull.com"
$message.To.Add($user.Email)
$message.Subject = $variant.Subject
$message.IsBodyHtml = $true
$message.Body = $personalizedHtml
# Add iPhone image with Content-ID
$attachment = New-Object System.Net.Mail.Attachment(".\iphone16e.jpg")
$attachment.ContentId = "iPhoneImage"
$attachment.ContentDisposition.Inline = $true
$message.Attachments.Add($attachment)
# Send email
$smtpClient = New-Object System.Net.Mail.SmtpClient($smtpServer, $smtpPort)
$smtpClient.Send($message)
Write-Host "✓ Sent '$($variant.Subject)' to $($user.FullName)
($($user.Department))"
-ForegroundColor Green
$message.Dispose()
} catch {
Write-Error "✗ Failed to send to $($user.Email): $($_.Exception.Message)"
}
}