How to send an email with embedded images using PowerShell - powershell

I want to send an automated email report with some embedded images that cannot be accessed externally from our network.
Google has lead me towards attaching the images and referencing them using CID Embedding in the body of the email using HTML.
I cannot find any guidance on how to do this, how do I set the content ID on an attached image so that it can be displayed in the HTML body?
Something along the lines of:
$html = "<ul><li><img src='cid:att'/></li><li>next img</li><li>another img</li></ul>"
Send-MailMessage -SmtpServer "stuff.co.uk" -to "person.co.uk" -from "mailbox.co.uk" -Subject "Report" -body $html -BodyAsHtml -Attachments \\some image from a server location.jpg

I got there in the end by adapting some code I found on reddit, it could definitely be improved but worked perfectly for my situation and because it's not the Base64 method, it shows the images on Outlook and Apple devices etc.
Hopefully this will help someone else in the future.
### Create a new mailmessage ###
$msg = new-object net.mail.mailmessage
$msg.From = 'email#address'
$msg.To.Add('email1, email2')
$msg.CC.Add('ccemail');
$msg.Subject = 'Daily Performance Report' + $(get-date -format dd/MM/yyyy)
$msg.IsBodyHtml = $true
#### Attach images with individual Content IDs (ContentId is used to refer to attachement inline in the email HTML body) ###
$attachment = New-Object System.Net.Mail.Attachment -ArgumentList "attachment file location e.g. C:\Users\username\documents\image.jpg"
$attachment.ContentDisposition.Inline = $true
$attachment.ContentId = 'image.jpg' #can be anything as long as it matches the CID reference in the HTML
$attachment.ContentDisposition.DispositionType = "Inline"
$attachment.ContentType.MediaType = "image/jpg"
$msg.Attachments.Add($attachment)
#repeat the above to add another attachment.
### Create the html body of the email ###
$msg.Body = "<p>Please see todays performance dashboards below. <p/>
<h2>Compliance Scores <h2/>
<h4 style='font-weight:normal' >Logon compliance scores for each environment<h4/>
<img src='cid:image.jpg' />"
### Create connection to smtp server and send the mail ###
$smtp = New-Object Net.Mail.SmtpClient('yoursmtp.server.com')
$smtp.Send($msg)
#$attachment.Dispose()
#$msg.Dispose()

Base64 might work for you. Just tried this code working with my client
function ImageToBase64($FilePath) {
$type = (dir $FilePath).extension -replace "\.",""
return $("data:image/$type;base64," + $([Convert]::ToBase64String([IO.File]::ReadAllBytes($FilePath))))
}
$html = "<ul><li><img src=`"" + $(ImageToBase64("c:\temp\image.jpg")) + "`"/></li><li><img src=`"" + $(ImageToBase64("c:\temp\nextimage.png")) + "`"/></li><li><img src=`"" + $(ImageToBase64("c:\temp\anotherimg.jpeg")) + "`"/></li></ul>"
Send-MailMessage -SmtpServer "stuff.co.uk" -to "person.co.uk" -from "mailbox.co.uk" -Subject "Report" -body $html -BodyAsHtml -Attachments \\some image from a server location.jpg
--Edit--
ATTENTION: I forgot to mention that not all clients will display base64 embedded images - thanks #Nathan

Related

Email multiple people individual attachments

I am using Send-MailMessage to email multiple different recipients each with an individual report. I have just been repeating the Send-Mail command with the different attachment paths for each recipient however the problem I am running into since I also have to use -UseSsl -credential I have to authenticate each time a new message is sent. Is there a way to authenticate once without having to do it each time?
Send-MailMessage is a wrapper for .net smtpclient. You can do your custom version of it, for example:
$client = New-Object System.Net.Mail.SmtpClient
$client.EnableSsl = $true
$client.Host = "smtp.server.com"
$client.Credentials = $creds
foreach ($r in $recipients) {
$from = "from#mail.com"
$to = $r
$msg = New-Object System.Net.Mail.MailMessage $from, $to
$msg.Subject = "subject"
$msg.Body = "body"
$msg.Attachments.Add("C:\temp\test.html")
$client.Send($msg)
}

Send mail via powershell without authentication

I was using nant to send mail and it is working fine - something like
<mail
from="Test#b.c"
tolist="A#b.c"
subject="Test"
mailhost="myhost.mydomain.com"
isbodyhtml="true"
message= "${Test}">
</mail>
I didn't have to use any kind of authentication.
Now when using powershell it seems I am forced to use authentication - something like this would fail:
Send-MailMessage -To $to -From $from -Subject "Test" –Body “Test (body) -SmtpServer "myhost.mydomain.com"
I would get the following message:
Send-MailMessage : No credentials are available in the security package
Am I missing some way to send mails without specifying credentials if the server supports that?
Edit:
I've also tried the answer here to send anonymous mails but it just times out:
send anonymous mails using powershell
Sending mails using Powershell v1 method works fine without authentication as shown here
My Powershell version is 5 yet this is apparently the way to go, unless someone has another idea.
$smtpServer = "ho-ex2010-caht1.exchangeserverpro.net"
$smtpFrom = "reports#exchangeserverpro.net"
$smtpTo = $to
$messageSubject = $subject
$messageBody = $body
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($smtpFrom,$smtpTo,$messagesubject,$messagebody)
I was looking for another issue and found this question here...
As #AngelicCore already explained one approach,
Here is another one if someone using Outlook dektop app...
The mail sent will appear in your outbox.
using outlook object
Try
{
#Email structure
$Outlook = New-Object -ComObject Outlook.Application
$Mail = $Outlook.CreateItem(0)
#Email Recipients
$Mail.To = "abc#domain.com;xyz#domain.com"
$Mail.Cc = "tuv#domain.com; pqr#domain.com"
#Email Subject
$date = Get-Date -Format g
$Mail.Subject = "Subject here $date"
#Email Body
$Mail.Body = "Body Here"
#Html Body
$Mail.HTMLBody == "<html> HTML Body Here </html>"
#Email Attachment
$file = "C:\path\xyz.txt"
$Mail.Attachments.Add($file)
$Mail.Send()
Write-Host -foreground green "Mail Sent Successfully"
}
Catch
{
write-host -foreground red $error[0].Exception.Message
}
pause
exit

function to send email multiple times

I am working on a PowerShell script which sends emails multiple times with different subject and body each time.
I am trying to move Send-MailMessage into a function or something that I could use to reduce the code lines.
$Sender = 'jones#example.com'
$text = "<html><body>"
$text += "<p>Welcome</p>"
### A cmdlet that would give recipient email address
$Recipient = (Get-Details -user $user).email
$smtp = "server.example.com"
$subject = "welcome email"
Send-MailMessage -BodyAsHtml $text -from $Sender -SmtpServer $smtp -Priority high -to $Recipient -Subject $subject
Write-Output "executing commands to capture results"
Write-Output ""
### Few Commands executed in this step
Write-Output "Analyzing results"
### Few commands executed in this step
$newtext = "<html><body>"
$newtext += "Congrats, you are selected"
$newsubject = "results email"
Send-MailMessage -BodyAsHtml $newtext -from $Sender -SmtpServer $smtp -Priority high -to $Recipient -Subject $subject
You could create a function like this:
Function Send-Email($text,$subject,$recipient)
{
Send-MailMessage -BodyAsHtml $text -From "jones#example.com"
-SmtpServer "server.example.com" -Priority High -To $recipient -Subject $subject
}
You can call it like:
Send-Email -text "Hello" -subject "Test" -recipient "test#example.com"
You can add or remove arguments depending on what will change though. Assuming the smtp server won't change for example, this isn't needed as a parameter.
I am trying to move Send-MailMessage into a function or something that I could use to reduce the code lines.
Writing a function for one line can be useful if the options are many and never change. However you do have one that changes. There is another PowerShell feature that would work here just as well. Splatting!
$emailParameters = #{
From = $Sender
SmtpServer = $smtp
Priority = "high"
To = $Recipient
Subject = $subject
}
Send-MailMessage -BodyAsHtml $text #emailParameters
# ... other code and stuff
Send-MailMessage -BodyAsHtml $newtext #emailParameters
Now you still only have to make changes in one place and the code is arguably more terse.
Another point is that when you are making multi-line strings all at once, as supposed to building over the course of the script you can always use here strings. You only have two lines but if you code evolves over time it is a good tactic to start early instead of many $object += "string" lines
$text = #"
<html><body>
<p>Welcome</p>
"#
Note that indentation is preserved in the resulting here-string. The "# has to appear on its own line with no leading whitespace. Using double quotes means you can still expand variables as well in there.

Add Attachment and Cc Parameter to smtp Email function

My powershell script currently handles sending an email from the command line to a list of users. I would like to add an attachment to the email and also a list of Cc so all emails don't come through the "To" email parameter. Here is my current function.
function SendEmail
{
$smtpServer = "smtp.server"
$smtpFrom = "PROD <email#gmail.com>"
$smtpTo = "me#gmail.com"
$messageSubject = "Weekly List "+$day
$messagebody = "Hi User, Please find attached document. Thank you."
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($smtpFrom,$smtpTo,$messageSubject,$messagebody)
}
My attempt was to add a variable called $attachment = "\PathtoFile"
and add $attachment into the Send function, but that didn't work...
Use Send-MailMessage with the Attachments and Cc parameter:
Send-MailMessage -Attachments "C:\path\to\attachment.ext" -Cc "myboss#gmail.com"
You can also specify an encoding (you mentioned special characters):
Send-MailMessage -Encoding UTF8
In general, I would recommend splatting the parameters, so it ends up looking like this:
$MyAttachments = ".\a\relative\path.ext","C:\another\file.ext"
$MailParams = #{
SmtpServer = "smtp.server"
From = "PROD <email#gmail.com>"
To = "me#gmail.com"
Subject = "Weekly List "+$day
Body = "Hi User, Please find attached document. Thank you."
Attachments = $MyAttachments
Encoding = [System.Text.Encoding]::UTF8
}
Send-MailMessage #MailParams

Funny issue using + in mail body

List item
Here is a weird one. $body is buildt with
a loop adding 30 - 100 lines:
$body += "+ $stuff `n"
The problem is if I use in $body a plus (+), it takes each line in the body and splits it to half the line size and continues it on the next line:
Example,
The line is actually:
moon|glowing so nice today|buy gold now and get really rich soon
It gets changed to (stackoverflow changed my (+) to a bullet. Just note that there is a plus in front of the next two lines).
moon|glowing so nice today|buy gold now
moon|and get really rich soon
If I change the plus (+) to the word Add, I get in the email body:
Add moon|glowing so nice today|buy gold now and get really rich soon
I would like to use the plus (+) as that is the same format another program uses with +/- of lines in the body of the email. Any ideas how to get this working?
function mailalert($body) {
$SMTPserver = "mailhost.domain.com"
$from = "replies-disabled#domain.com"
$to = "joe#domain.com"
$subject = "Report for Stuff"
$mailer = new-object Net.Mail.SMTPclient($SMTPserver)
$msg = new-object Net.Mail.MailMessage($from, $to, $subject, $body)
$mailer.send($msg)
}
Okay, I abandoned Net.Mail.SMTPclient and decided to use html as body of text to get around this:
function mailalert($diff) {
$SMTPserver = "mailhost.domain.com"
$smtp = New-Object system.Net.Mail.SmtpClient($SMTPserver)
$mail = New-Object System.Net.Mail.MailMessage
$mail.From = "replies-disabled#domain.com"
$mail.To.Add("joe#domain.com")
$mail.Subject = "Report for stuff"
$mail.Body = $diff
$mail.IsBodyHtml = $true
$smtp.Send($mail)
}
I changed all use of `n to and tada it works. It still is a bit perplexing why the other way did not work. I see another issue where email addresses with pluses in them was being addressed, so I wonder if fixing that broke this. :)
Try:
$body = "$body $stuff `n"
in your loop.
Escape any troublesome characters using a " ` ".
$body = "$body `+ $stuff `n"