Add HTML body to an email with Powershell - html

I'm using powershell to notify a high number of users about an expiration date of a document.
I have my script prepared
$smtpServer = "xxxxx.contoso.com"
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = New-Object Net.Mail.mailaddress "xxxx#contoso.es", "xxx yyy zzz"
$msg.ReplyTo = "no-reply#contoso.es"
$msg.To.Add("xxx#contoso.es")
$msg.bcc.add($bcc)
$msg.subject = "Expiration notification"
$msg.body = I need to include HTML content, or MHT or bin as outlook message format.
$smtp.Send($msg)
But the email body was preformatted using outlook, so it contains images, preformated text etc. Which would be the best way to include that kind of content in the message body??
Thank you!

It should work if you add the Body as an html string and also add the line:
$msg.IsBodyHTML = true

Related

Embed images in email and send via Powershell

I am editing one of my old scripts to send an email to a user with images embedded into the text. I am trying to use the Send-MailMessage function to send the email as opposed to the older method of $smtp.send($msg). However, when trying to update the script, the images are no longer being embedded.
I know how to attach them to the email as actual attachments, but I am not sure what I am doing wrong to have them show as actual embedded images.
NOTE: for brevity, I removed some of the full email since it is large and as long as I can get an image or two working, it will all work.
# force powershell to run as an x86 process
Set-ExecutionPolicy -Scope CurrentUser Unrestricted
if ($env:Processor_Architecture -ne "x86") {
&"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" -file $myinvocation.Mycommand.path
exit
}
# initialize the script
if ($startupvariables) { try {Remove-Variable -Name startupvariables -Scope Global -ErrorAction SilentlyContinue } catch { } }
New-Variable -force -name startupVariables -value ( Get-Variable | ForEach-Object { $_.Name } )
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$Separator = ".", "#"
# advise what the script does
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OKCancel
$MessageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageTitle = "Shared Drive Access Assistance"
$MessageBody = "This script asks the user to provide more information regarding a network drive that they would like access to.`n`nTo use it, enter the below information:`n`n`n`tTicket Number`n`n`tUser's Email Address`n`n`tRequestor's Email Address`n`n`nIf this is the script you want to use, click OK.`nIf not, click Cancel."
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
if ($Result -eq "Cancel")
{
Exit-PSSession
}
else
{
# get the ticket number
$Ticket = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the SCTask ticket number" , "Ticket Number")
# get the user id via the email address
$UserID = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the user's email address" , "User Email Address")
$User = $UserID.split($Separator)
$Firstname = $User[0].substring(0,1).toupper()+$User[0].substring(1).tolower()
$Lastname = $User[1].substring(0,1).toupper()+$User[1].substring(1).tolower()
$User = $Firstname, $Lastname
# get local username
$Username = [System.Environment]::UserName
# create email
$subject = "Ticket $Ticket on Hold for User Response - Shared Drive Access for $User - Awaiting Additional Information"
$body = #"
<html>
<body style="font-family:calibri">
To $Requestor, $User,<br>
<br>
<br>
In order to proceed with your request for shared drive access, we require the server name and full path to the folder you need access to. If you do not already know this information, you will need to provide these instructions to someone that already has access to the folder that you need access to.<br>
<br>
1) Click the Start menu<br>
<br>
<img src="cid:image1.png"><br>
<img src="cid:image2.png"><br>
<img src="cid:image3.png"><br>
<br>
<br>
2) Navigate to "Computer"<br>
<br>
<img src="cid:image4.png"><br>
<br>
<br>
<br>
If you have any questions or need assistance with this process, please contact the Service Desk via one of the methods listed below.
<br>
<br>
Thank You,<br>
<br>
IT Service Desk<br>
</body>
</html>
"#
$att1 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive1.png")
$att2 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive2.png")
$att3 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive3.png")
$att4 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive4.png")
$att1.ContentId = "image1.png"
$att2.ContentId = "image2.png"
$att3.ContentId = "image3.png"
$att4.ContentId = "image4.png"
# $msg.Attachments.Add($att1)
# $msg.Attachments.Add($att2)
# $msg.Attachments.Add($att3)
# $msg.Attachments.Add($att4)
# create confirmation message
$ButtonType = [System.Windows.MessageBoxButton]::YesNo
$MessageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageTitle = "Shared Drive Access Assistance"
$MessageBody = "The information you have entered is show below:`n`n`nTicket Number: $Ticket`n`nUser's Email Address: $UserID`n`nRequstor's Email Address: $RequestorID`n`n`nIf you would like to send the email, click Yes.`nOtherwise, click No."
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
if ($Result -eq "No")
{
Exit-PSSession
}
else
# send email
{
Send-MailMessage -To "<$UserID>" -bcc "<$Username#dana.com>" -from "<itservicedesk#x.com>" -Subject $global:subject -SmtpServer "mailrelay.x.com" -BodyAsHtml -body $global:body
}
}
Function Clean-Memory {
Get-Variable |
Where-Object { $startupVariables -notcontains $_.Name } |
ForEach-Object {
try { Remove-Variable -Name "$($_.Name)" -Force -Scope "global" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue}
catch { }
}
}
So the real question is how to embed a image into the HTML document from a attachment
CID aka Content ID will allow you to attach a image and then use that attached image in the document. Avoid using spaces in the Content ID name.
Send-MailMessage -To "Test#Test.com" -from "Test2#Test.com" -SmtpServer SMTP.TEST.NET -Subject "Hello" -BodyAsHtml -Body "<img src='cid:Test.png'>" -Port 25 -Attachments "C:\Users\Test\Test.png"
You are using
$att1 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive1.png")
$att2 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive2.png")
$att3 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive3.png")
$att4 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive4.png")
$att1.ContentId = "image1.png"
$att2.ContentId = "image2.png"
$att3.ContentId = "image3.png"
$att4.ContentId = "image4.png"
but when you send the mail you are not attaching those attachments
Send-MailMessage -To "<$UserID>" -bcc "<$Username#dana.com>" -from "<itservicedesk#x.com>" -Subject $global:subject -SmtpServer "mailrelay.x.com" -BodyAsHtml -body $global:body
You could stop using the Net.Mail.Attachment and instead do something like
$Body = #"
<html>
<body style="font-family:calibri">
<b>This is image 1</b>
<img src='cid:TEST1.png'>
<b>This is image 2</b>
<img src='cid:Test2.png'>
</body>
</html>
"#
Send-MailMessage -To "Test#Test.com" `
-from "Test2#Test.com" `
-SmtpServer Test.smtp.com `
-Subject "Hello" `
-BodyAsHtml -body $body `
-Attachments "C:\Test\TEST1.png", "C:\Test\TEST2.png"
You can embed the image into the HTML, therefore there are no extra files.
It is replacing
{img=file}
with data URL
{src="data:image/png;base64,[base64 encoded long string representing the image]}
There are many online converters that can convert your image file into the required code.
Just google for Image to Data URI converter.
Here is a simple example using MailKit. I had do download the packages from https://www.nuget.org/packages/MimeKit and https://www.nuget.org/packages/MailKit and unpack with 7Zip as PowerShell was struggling to install from the CLI. I got some ideas from here but wanted to add an example of an embedded image, https://adamtheautomator.com/powershell-email.
You can do all sorts with MimeKit and MailKit including modern Auth which is great. It's also supper fast and the Client and even a Connection can be used in a loop to send lots of messages. There is probably an upper bound on how long to use a connection without refreshing?
$mailServer = "domain-com.mail.protection.outlook.com"
$SMTP = New-Object MailKit.Net.Smtp.SmtpClient
Add-Type -Path ".\mailkit.3.3.0\netstandard2.0\MailKit.dll"
Add-Type -Path ".\mimekit.3.3.0\netstandard2.0\MimeKit.dll"
$SMTP.Connect($mailServer, 25, [MailKit.Security.SecureSocketOptions]::StartTls, $False)
$Message = New-Object MimeKit.MimeMessage
$message.From.Add("a.person#domain.com");
$message.To.Add("b.person#domain.com");
$message.Subject = "Some Subject";
$builder = [MimeKit.BodyBuilder]::new();
$image = $builder.LinkedResources.Add($path);
$image.ContentId = [MimeKit.Utils.MimeUtils]::GenerateMessageId();
$body = #"
<html>
<body style="font-family:calibri">
<b>This is image 1</b>
<img src='cid:$($image.ContentId)'>
</body>
</html>
"#
$builder.HtmlBody = $body
#Now we just need to set the message body and we're done
$message.Body = $builder.ToMessageBody();
$SMTP.Send($Message)
$SMTP.Disconnect($true)
$SMTP.Dispose()

importing HTML and outputting with variables

I'm looking for a method to import a template HTML file in PowerShell and being able to populate it with variables, which in turn sends an e-mail in HTML format containing user data.
I already know how to send the HTML e-mail. And, currently have HTML in a 'here' string embedded in the code. I want to take it a step further, by being able to grab an HTML template based on country code. So, if the user is in the US, it'll get a English HTML data filled e-mail, if they're dutch, they'll get it in Dutch, etc.
function SendMessage {
Param(
[Parameter(Position=0,Mandatory=$true)]
[string]$Identity,
[Parameter(Position=1,Mandatory=$true)]
[string]$Body
)
$Subject = "Important information - Do not delete this email. Welcome to Voicemail"
$SmtpClient = New-object system.net.mail.smtpClient
$MailMessage = New-Object system.net.mail.mailmessage
$CredentialFile = ".\UMcloud-creds.txt"
$password = Get-Content $CredentialFile| ConvertTo-SecureString -Force
$UMCloudAdmin = ""
$SmtpClient.Credentials = New-Object System.Net.NetworkCredential($UMCloudAdmin, $Password)
$smtpclient.Host = "smtp-in.net"
$MailMessage.From = "Voicemail <P#domain.net>"
$MailMessage.To.clear()
$MailMessage.To.Add($Identity)
$MailMessage.Subject = $Subject
$Mailmessage.Body = $body
$MailMessage.IsBodyHtml = 1
$Logofilepath = ((Resolve-Path .\).Path) + "\logo.jpg"
$attachment = New-Object System.Net.Mail.Attachment -ArgumentList $LogoFilePath
$attachment.ContentDisposition.Inline = $True
$attachment.ContentDisposition.DispositionType = "Inline"
$attachment.ContentType.MediaType = "image/jpg"
$attachment.ContentId = "logo.jpg"
$MailMessage.Attachments.Add($attachment)
do {
$Continue = $false
try {
$smtpclient.Send($MailMessage)
Write-LogFile $OutputLogFile ("[SUCCESS] {0} {1}" -f $identity, $UMExtension)
Write-LogFile $customemaillog ("[SUCCESS] {0} {1}" -f $identity, $UMExtension) | out-null
$Continue = $true
} catch {
sleep -s 10
Write-LogFile $OutputLogFile "[ERROR] $Identity $_.Exception.Message"
Write-LogFile $CustomEmailLog "[ERROR] $Identity $UMExtension"
Write-Error $_.Exception.Message
}
} until($Continue -eq $true)
} # End send message
$WelcomeText = Get-Content -Path ".\$CountryID.txt"
$Body = #"
<html>
...
</html>
"#
The here string is part of the script, I'd like to be able to import it from TXT file as not to clutter the script.
Instead of using a text file, create a powershell file (ps1) for each html language format you want. Within those files, you can set a single variable as the html text (file EN_US.ps1):
$bodyENUS = #"Dear <b><font color=red>user</b></font> <br>
This is a test <b>HTML</b> email for your language preference<br>
Sincerely,<br> PdeRover<br>"
You can then pass the variable into the main ps file using two ways: Dot Sourcing or using a Global Variable.
Dot Sourcing: calling the variable by providing the file name.
In the main file:
..\EN_US.ps1
..\PT_PT.ps1
$smtp = "Exchange-Server"
$to = $Identity
$from = "Voicemail <P#domain.net>"
$subject = "This is a Test of HTML Email"
if (user is English speaking){
$bodyByLang = $bodyENUS}
elseif (user is Portuguese speaking) {
$bodyByLang = $bodyPTPT}
send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $bodyByLang -BodyAsHtml -Priority high
Global Variable: prefixing a variable with $Global:and calling the file during runtime. $Global: bodyENUS Then calling it using $bodyENUS
I asked my own SO question about the difference/best practice of using them. May be worth a read.
This should be enough to point you in the right direction.

embedded html powershell email compressing length of pic [duplicate]

I have a powershell script that embedds (not attaches) a picture and sends an email. The picture has increased now to 1500x5000 pixels and now I'm seeing that the pictures lenth gets compressed and it distorts the picture. How ever, when I manually insert the picture via outlook and send an email, it looks fine.
If i save the picture and then open it via paint or something, the picture opens fine. It just looks compressed in the email. Anyone know what may be going on there?
{
$Application = "C:\Autobatch\Spotfire.Dxp.Automation.ClientJobSender.exe"
$Arguments = "http://s.net:8070/spotfireautomation/JobExecutor.asmx C:\Autobatch\HourlyStats.xml"
$CommandLine = "{0} {1}" -f $Application,$Arguments
invoke-expression $CommandLine
$file = "C:\Autobatch\HourlyStats.png"
$smtpServer = "smtp.staom.sec.s.net"
$att = new-object Net.Mail.Attachment($file)
$att.ContentType.MediaType = “image/png”
$att.ContentId = “pict”
$att.TransferEncoding = [System.Net.Mime.TransferEncoding]::Base64
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.Attachments.Add($att)
$msg.From = "d.k#s.com"
$msg.To.Add("r.p#p.com")
$msg.Subject = "Voice and Data Hourly Stats"
$msg.Body = "<p style=’font-family: Calibri, sans-serif’>
Voice and data hourly stats details<br />
</p>
<img src='cid:pict'/>"
$msg.IsBodyHTML = $true
$smtp.Send($msg)
$att.Dispose()
invoke-expression "DEL $file"
}
here is what the picture looks like in the email.
Try adding
$att.ContentDisposition.Inline = $true
I suspect some default behavior is happening under the covers and it's just not consistent between the script and Outlook.
More info here
It seems like your email client shrinks content to a certain maximum size. Try putting <img src='cid:pict'/> in a <div> environment:
<div style="overflow: scroll">
<img src='cid:pict'/>
</div>
Also, if you have any way to retrieve the actual pixel width of the image, you can try to set the CSS of the <img> tag accordingly.
By asking this I may sound like a noob, but Just out of Curiosity, if you have a manual way to send an email via Outlook, why not to make a script to send an automated email with desired screenshot?
IDK, if this might help you or not, but I had made this script long back, for my daily reporting purposes. Well, it fits the bill. Sharing it here, for your views on it.
#In this segment, I navigate IE to my specific destination, screen which I want to capture.
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $true;
$Website = $ie.navigate('https://put.your.URL.here')
while($Website.Busy){Start-Sleep -Seconds 5}
#In this class, script captures the screen, once, all the data loading is over.
$file = "C:\Users\Desktop\$(Get-Date -Format dd-MM-yyyy-hhmm).bmp"
#P.S. I made it to save that screenshot with current date and time format. Also, default screenshot will be captured in .BMP format.
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$width = $Screen.width
$Height = $Screen.Height
$Left = $Screen.Left
$Right = $Screen.Right
$Top = $Screen.Top
$Bottom = $Screen.Bottom
$bitmap = New-Object System.Drawing.Bitmap $width, $Height
$Graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$Graphics.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)
$bitmap.Save($File)
Write-Output "Screenshot saved to:"
Write-Output $File
sleep -Seconds 5
#Sending an Email
$Outlook = New-Object -ComObject Outlook.Application
$mail = $Outlook.CreateItem(0)
$mail.To = "your.designated#emailid.com"
$mail.Subject = "Outstanding data as on $(Get-Date -Format dd-MM-yyyy)"
$mail.Body = "PFA screenshot, of all outstanding formation as on $(Get-Date -Format dd-MM-yyyy-hhmm)"
$mail.Attachments.Add($file)
$mail.Send()
I am just answering this, since, I tried commenting above, but I guess, my reputation score is way too less to do that.
Hope this might be helpful for you to find a workaround.
Happy coding. :)
HTML code: is <img src='cid:pict'/> supposed to be <img src='cid:pict'> -just remove the forward slash?
Added: This link might help talking about embedding pic in email. base64 encoded images in email signatures. You can try generation base64 code and put it in email body HTML.

How to embed Images in a powershell email using MailMessage

I have an email that works from PS. What I have been trying to do is include images embedded in the email (not attachments). Below is what I have so far:
function Email
{
$smtpServer = {smtp server}
$smtpFrom = {email from}
$smtpTo = {email to}
$messageSubject = "test"
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
$message.Subject = $messageSubject
$message.IsBodyHTML = $true
$credentials=new-object system.net.networkcredential({smtpUsername},{smtpPassword})
# $message.Body = Get-Content "D:\Program Files\CymbaTech_FBNC_AM\CTDataLoader\data\TestBody.html"
# The line below will add any attachments you have such as log files.
$message.Attachments.Add("{path}\Image1.png")
$message.Body = '<img src="cid:xyz">'
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.credentials=$credentials.getcredential($smtpserver,"25","basic")
$smtp.Send($message)
}
In the above, I have added image tags to the Body.html file. If I open the html directly, it looks as expected with images showing correctly.
When I send the mail however, the images are just displayed as white boxes with a border. Seems that the script is not loading the images in the file.
Has anyone done similar before and have any suggestions?
You must add images as regular attachments. The HTML body must then reference these attachments though the cid attribute: <img src="cid:xyz"> where "xyz" is the value of the Content-ID MIME attribute on the attachment MIME part.

embedded html powershell email compressing pic length

I have a powershell script that embedds (not attaches) a picture and sends an email. The picture has increased now to 1500x5000 pixels and now I'm seeing that the pictures lenth gets compressed and it distorts the picture. How ever, when I manually insert the picture via outlook and send an email, it looks fine.
If i save the picture and then open it via paint or something, the picture opens fine. It just looks compressed in the email. Anyone know what may be going on there?
{
$Application = "C:\Autobatch\Spotfire.Dxp.Automation.ClientJobSender.exe"
$Arguments = "http://s.net:8070/spotfireautomation/JobExecutor.asmx C:\Autobatch\HourlyStats.xml"
$CommandLine = "{0} {1}" -f $Application,$Arguments
invoke-expression $CommandLine
$file = "C:\Autobatch\HourlyStats.png"
$smtpServer = "smtp.staom.sec.s.net"
$att = new-object Net.Mail.Attachment($file)
$att.ContentType.MediaType = “image/png”
$att.ContentId = “pict”
$att.TransferEncoding = [System.Net.Mime.TransferEncoding]::Base64
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.Attachments.Add($att)
$msg.From = "d.k#s.com"
$msg.To.Add("r.p#p.com")
$msg.Subject = "Voice and Data Hourly Stats"
$msg.Body = "<p style=’font-family: Calibri, sans-serif’>
Voice and data hourly stats details<br />
</p>
<img src='cid:pict'/>"
$msg.IsBodyHTML = $true
$smtp.Send($msg)
$att.Dispose()
invoke-expression "DEL $file"
}
here is what the picture looks like in the email.
Try adding
$att.ContentDisposition.Inline = $true
I suspect some default behavior is happening under the covers and it's just not consistent between the script and Outlook.
More info here
It seems like your email client shrinks content to a certain maximum size. Try putting <img src='cid:pict'/> in a <div> environment:
<div style="overflow: scroll">
<img src='cid:pict'/>
</div>
Also, if you have any way to retrieve the actual pixel width of the image, you can try to set the CSS of the <img> tag accordingly.
By asking this I may sound like a noob, but Just out of Curiosity, if you have a manual way to send an email via Outlook, why not to make a script to send an automated email with desired screenshot?
IDK, if this might help you or not, but I had made this script long back, for my daily reporting purposes. Well, it fits the bill. Sharing it here, for your views on it.
#In this segment, I navigate IE to my specific destination, screen which I want to capture.
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $true;
$Website = $ie.navigate('https://put.your.URL.here')
while($Website.Busy){Start-Sleep -Seconds 5}
#In this class, script captures the screen, once, all the data loading is over.
$file = "C:\Users\Desktop\$(Get-Date -Format dd-MM-yyyy-hhmm).bmp"
#P.S. I made it to save that screenshot with current date and time format. Also, default screenshot will be captured in .BMP format.
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$width = $Screen.width
$Height = $Screen.Height
$Left = $Screen.Left
$Right = $Screen.Right
$Top = $Screen.Top
$Bottom = $Screen.Bottom
$bitmap = New-Object System.Drawing.Bitmap $width, $Height
$Graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$Graphics.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)
$bitmap.Save($File)
Write-Output "Screenshot saved to:"
Write-Output $File
sleep -Seconds 5
#Sending an Email
$Outlook = New-Object -ComObject Outlook.Application
$mail = $Outlook.CreateItem(0)
$mail.To = "your.designated#emailid.com"
$mail.Subject = "Outstanding data as on $(Get-Date -Format dd-MM-yyyy)"
$mail.Body = "PFA screenshot, of all outstanding formation as on $(Get-Date -Format dd-MM-yyyy-hhmm)"
$mail.Attachments.Add($file)
$mail.Send()
I am just answering this, since, I tried commenting above, but I guess, my reputation score is way too less to do that.
Hope this might be helpful for you to find a workaround.
Happy coding. :)
HTML code: is <img src='cid:pict'/> supposed to be <img src='cid:pict'> -just remove the forward slash?
Added: This link might help talking about embedding pic in email. base64 encoded images in email signatures. You can try generation base64 code and put it in email body HTML.