powershellemailstringbuildermailmessagesystem.net

Attaching a attachment to a calendar event using powershell


I am creating a calendar event using powershell. Ideally, I'd like to avoid using modules if possible because I feel like what I'm trying to do is very possible with what I have, but if a module is my best option I am open to trying it.

Here are a couple of things I am trying to accomplish.

  1. Attach a calendar invite
  2. Ideally I'd like an HTML or RTF email so I can format the body of the text nicely.
  3. I'd like to attach 2 files to the body of the email with the calendar invite.

Here are the hurdles I am running in to. I was able to complete 1 & 2 and everything is working great, but if I try to add an attachment to the file, it either breaks the calendar, or it doesn't attach the file.

Here is what I have tried. If I try attaching the file as a mail attachment see example below (this attaches the file to the mail, but it break my calendar)

$attachment = New-Object System.Net.Mail.Attachment -ArgumentList $File1
$Mail.Attachments.Add($attachment)

If I try to encode the attachment in my email and pass the variable into my string builder and attach it that way, it doesn't seem to work properly (no attachment), but the calendar seems to still send fine.

Here is what I am working with as an example:

CLS

$Path = (Split-Path $script:MyInvocation.MyCommand.Path)

$SmtpClient = new-object system.net.mail.smtpClient
$SmtpClient.Host = "SMTP.domain.com"

$Mail = New-Object System.Net.Mail.MailMessage

$Mail.From = "someone@domain.com"
$Mail.To.Add("me@domain.com")
$Mail.IsBodyHtml = $true

#====================== Files we will be attaching to our Calendar invite

if($NULL -eq $File1)
{
    $File1 = "$Path\File1.docx"
}
if($NULL -eq $File2)
{
    $File2 = "$Path\File2.xlsx"
}

#We will encode our files above into the calendar invite

if($NULL -eq $EncFile1)
{
    $EncFile1 = [convert]::ToBase64String((Get-Content -path "$File1" -Encoding byte))
}

if($NULL -eq $EncFile2)
{
    $EncFile2 = [convert]::ToBase64String((Get-Content -path "$File2" -Encoding byte))
}

#======================

$Body = "<html xmlns:v='urn:schemas-microsoft-com:vml' xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns:m='http://schemas.microsoft.com/office/2004/12/omml' xmlns='http://www.w3.org/TR/REC-html40'><head><META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=us-ascii'><meta name=Generator content='Microsoft Word 15 (filtered medium)'><style><!--
/* Font Definitions */
@font-face
    {font-family:'Cambria Math';
    panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
    {font-family:Calibri;
    panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
    {margin:0in;
    margin-bottom:.0001pt;
    font-size:11.0pt;
    font-family:'Calibri',sans-serif;}
a:link, span.MsoHyperlink
    {mso-style-priority:99;
    color:#0563C1;
    text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
    {mso-style-priority:99;
    color:#954F72;
    text-decoration:underline;}
span.EmailStyle17
    {mso-style-type:personal-compose;
    font-family:'Calibri',sans-serif;
    color:windowtext;}
.MsoChpDefault
    {mso-style-type:export-only;
    font-family:'Calibri',sans-serif;}
@page WordSection1
    {size:8.5in 11.0in;
    margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
    {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext='edit' spidmax='1026' />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext='edit'>
<o:idmap v:ext='edit' data='1' />
</o:shapelayout></xml><![endif]--></head><body lang=EN-US link='#0563C1' vlink='#954F72'><div class=WordSection1><p class=MsoNormal><b>Header:<o:p></o:p></b></p><p class=MsoNormal>This is a test<o:p></o:p></p><p class=MsoNormal><o:p>&nbsp;</o:p></p><p class=MsoNormal><o:p>&nbsp;</o:p></p><p class=MsoNormal><b>HELLO WORLD!!!!<o:p></o:p></b></p><p class=MsoNormal><o:p>&nbsp;</o:p></p></div></body></html>"

$Mail.Body = $Body
$Mail.Subject = "This is just a test"

#String Builder Stuff
$sb = [System.Text.StringBuilder]::new()
$sb.AppendLine("BEGIN:VCALENDAR") | Out-Null
$sb.AppendLine("PRODID:-//Schedule a Meeting") | Out-Null
$sb.AppendLine("VERSION:2.0") | Out-Null
$sb.AppendLine("METHOD:REQUEST") | Out-Null

#Configuration for correct Timezone
$sb.AppendLine("BEGIN:VTIMEZONE") | Out-Null
$sb.AppendLine("TZID:Central Standard Time") | Out-Null
$sb.AppendLine("BEGIN:STANDARD") | Out-Null
$sb.AppendLine("DTSTART:16011104T020000") | Out-Null
$sb.AppendLine("RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11") | Out-Null
$sb.AppendLine("TZOFFSETFROM:-0500") | Out-Null
$sb.AppendLine("TZOFFSETTO:-0600") | Out-Null
$sb.AppendLine("END:STANDARD") | Out-Null
$sb.AppendLine("BEGIN:DAYLIGHT") | Out-Null
$sb.AppendLine("DTSTART:16010311T020000") | Out-Null
$sb.AppendLine("RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3") | Out-Null
$sb.AppendLine("TZOFFSETFROM:-0600") | Out-Null
$sb.AppendLine("TZOFFSETTO:-0500") | Out-Null
$sb.AppendLine("END:DAYLIGHT") | Out-Null
$sb.AppendLine("END:VTIMEZONE") | Out-Null

$sb.AppendLine("BEGIN:VEVENT") | Out-Null

#Attachments to the calendar invite
$sb.AppendLine("ATTACH;ENCODING=BASE64;VALUE=BINARY;X-FILENAME=File1.docx:$EncFile1") | Out-Null
$sb.AppendLine("ATTACH;ENCODING=BASE64;VALUE=BINARY;X-FILENAME=File2.xlsx:$EncFile2") | Out-Null

$sb.AppendLine([String]::Format("DTSTART;TZID='Central Standard Time':{0:yyyyMMddTHHmmssZ}",[datetime]::Now.AddMinutes(30))) | Out-Null
$sb.AppendLine([String]::Format("DTSTAMP;TZID='Central Standard Time':{0:yyyyMMddTHHmmssZ}", [datetime]::Now)) | Out-Null
$sb.AppendLine([String]::Format("DTEND;TZID='Central Standard Time':{0:yyyyMMddTHHmmssZ}", [datetime]::Now.AddMinutes(60))) | Out-Null
$sb.AppendLine("LOCATION: " + "Secret Boardroom") | Out-Null
$sb.AppendLine([String]::Format("UID:{0}", $(New-Guid).Guid)) | Out-Null
$sb.AppendLine([String]::Format("DESCRIPTION:{0}", $Mail.Body)) | Out-Null
$sb.AppendLine([String]::Format("X-ALT-DESC;FMTTYPE=text/html:{0}", $Mail.Body)) | Out-Null
$sb.AppendLine([String]::Format("SUMMARY:{0}", $Mail.Subject)) | Out-Null
$sb.AppendLine([String]::Format("ORGANIZER:MAILTO:{0}", $Mail.From.Address)) | Out-Null

$sb.AppendLine([String]::Format("ATTENDEE;RSVP=TRUE:mailto:{0}", $Mail.To[0].Address)) | Out-Null

$sb.AppendLine("BEGIN:VALARM") | Out-Null
$sb.AppendLine("TRIGGER:-PT15M") | Out-Null
$sb.AppendLine("ACTION:DISPLAY") | Out-Null
$sb.AppendLine("DESCRIPTION:Reminder") | Out-Null
$sb.AppendLine("END:VALARM") | Out-Null
$sb.AppendLine("END:VEVENT") | Out-Null
$sb.AppendLine("END:VCALENDAR") | Out-Null

#Configure mimetype to support our Calendar within the body of the email
$contype = New-Object System.Net.Mime.ContentType("text/calendar")
$contype.Parameters.Add("method", "REQUEST")
$contype.Parameters.Add("name", "Test.ics");
$avCal = [Net.Mail.AlternateView]::CreateAlternateViewFromString($sb.ToString(),$contype)

#Configure mimetype to support HTML in the body of the email
$EnableHTML = New-Object System.Net.Mime.ContentType("text/html")
$aHTML = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body.ToString(),$EnableHTML)

$Mail.AlternateViews.Add($aHTML)
$Mail.AlternateViews.Add($avCal)

#Send Email
$SmtpClient.Send($Mail)

#Clear resources
$Mail.Dispose()
$SmtpClient.Dispose()

Any help would be greatly appreciated. Hopefully, I can easily add the 2 file attachments without breaking he calendar invite or the message. If I have to part with any of the features I listed above, for example the HTML in the body of the email, then I am fine with that. The attachments and the calendar the most important part, but the formatting would be a "nice to have" in the body of the email.


Solution

  • Figured out a way to attach files. Not sure if there is a better way, but I had to create a new linked resource. Here are 2 examples of attaching an xlsx and a docx file.

    To get the mime types I used this link as a reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types

    $testFile2 = New-Object System.Net.Mail.LinkedResource($File2, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
    $testFile2.ContentId = "Test2"
    $testFile2.ContentType.Name = "Test2.xlsx"
    
    $testFile1 = New-Object System.Net.Mail.LinkedResource($File1, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
    $testFile1.ContentId = "Test1"
    $testFile1.ContentType.Name = "Test1.docx"
    
    $aHTML.LinkedResources.Add($testFile1)
    $aHTML.LinkedResources.Add($testFile2)