How to Send a Welcome Message to New Employees with Attachments for Calendar Events

Creating ICS Files for Online Meetings that Outlook Likes is More Difficult Than You Might Think

Writing a PowerShell script to send a welcome message to new employees is a classic piece of automation. The first script probably appeared soon after Exchange 2007 shipped with support for finding new mailboxes nearly twenty years ago. Since then, administrators have coded countless variations, so what new knowledge could be discovered by going down this path again?

My interest was piqued by a question in a Reddit forum where the request was to send details of corporate events like company town halls and CEO fireside chats along with the welcome message. Scripting sending attachments with email is not new and is covered in-depth here. What was new was how to send details of online Teams meetings that the user could add to their calendar. Some ideas include:

  • Include the details of the corporate events in the message body. In this scenario, the user is required to manually add the events to their calendar.
  • Include an ICS (iCalendar) file for each corporate event as an attachment. The user can open the ICS files to add each event to their calendar.

The first approach is easy. The second has some challenges. Let’s see what they might be.

ICS Files

iCalendar (ICS) files are designed to support interoperability of scheduling and calendar information. The files are plain text with their content defined in RFC5545. In this case, the task is to extract events from a corporate calendar, format each event in an ICS file, and add the files as attachments to the welcome message. The ICS files must include the information necessary to create an online meeting in a user calendar when imported by Outlook. As we’ll discuss, some effort is needed to create the contents of an ICS file in an acceptable format for Outlook to import.

The task to generate an ICS that contains the details of the online meeting so that the join URL is included in the event created in the user’s calendar. Conceptually, a simple ICS might look like this:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PowerShell//iCalendar//EN
METHOD:PUBLISH
BEGIN:VEVENT
UID:43b8598c-a60d-4645-b331-b5b9e155d6ee
DTSTAMP:20260512T174634Z
DTSTART:20260519T070000Z
DTEND:20260519T080000Z
SUMMARY:New Entrants Onboarding Event 
DESCRIPTION:Microsoft Teams Meeting
https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZWQyMzA3M2UtNWMzNS00ZmI3LThiNmUtNmRlOGFlMjVlMTQz%40thread.v2/0?context=%7b%22Tid%2
2%3a%22b662313f-14fc-43a2-9a7a-d2e27f4f3478%22%2c%22Oid%22%3a%220b831b05-b38b-4bc9-a8b5-ea3cb467c963%22%7d
LOCATION:Microsoft Teams
URL:https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZWQyMzA3M2UtNWMzNS00ZmI3LThiNmUtNmRlOGFlMjVlMTQz%40thread.v2/0?context=%7b%22Tid%22%3a%22b662313f-14fc-43a2-9a7a-d2e27f4f3478%22%2c%22Oid%22%3a%220b831b05-b38b-4bc9-a8b5-ea3cb467c963%22%7d
END:VEVENT
END:VCALENDAR

Finding User Accounts Created in the Last Week

Some versions of scripts to send email to welcome new users already exist in the Office 365 for IT Pros GitHub repository (here’s an example). However, the scripts use Exchange Online to figure out the set of new mailboxes to receive welcome messages.

Scripts that process objects at periodic intervals like every week or month are prime candidates to run as scheduled Azure Automation runbooks. To minimize the number of modules in the runtime environment, I decided to only use the Microsoft Graph PowerShell SDK. Instead of using the Get-Mailbox cmdlet to filter by mailbox creation date, I used the Get-MgUser cmdlet to find member accounts created in the last 7 days that have a license which includes an Exchange Online service plan:

# Find the set of user accounts licensed for Exchange Online created in the last
# seven days
$WeekAgo = (Get-Date).AddDays(-7).toString("yyyy-MM-ddTHH:mm:ssZ")    
$ExoServicePlan1 = "9aaf7827-d63c-4b61-89c3-182f06f82e5c"
$ExoServicePlan2 = "efb87545-963c-4e0d-99df-69c6916d9eb0"

[array]$Users = Get-MgUser -Filter "(createddateTime ge $WeekAgo and userType eq 'Member') and (assignedPlans/any(c:c/servicePlanId eq $ExoServicePlan1 and capabilityStatus eq 'Enabled') or assignedPlans/any(c:c/servicePlanId eq $ExoServicePlan2 and capabilityStatus eq 'Enabled'))" ConsistencyLevel eventual -CountVariable Test -All -PageSize 500 -Sort ('displayname') Property Id, displayName, userprincipalName, assignedPlans, CreatedDateTime, mail

The set returned by Get-MgUser can include shared mailboxes if those mailboxes are assigned Exchange licenses (to use an archive mailbox or get increased storage quota), but that’s OK.

Finding Corporate Events

We need a calendar to store corporate events. The calendar can be hosted by a shared mailbox or a user mailbox, and its purpose is to schedule corporate events and meetings. The script finds events for the next six weeks and creates an ICS file for each event that ends up as an attachment.

The essential steps are to define the calendar account and calendar to search, define start and end dates for the search, and run the Get-MgUserCalendarView cmdlet  to fetch the events:

$CalendarUser = Get-MgUser -UserId Corporate.Events.Meetings@office365itpros.com
$Calendar = Get-MgUserDefaultCalendar -UserId $CalendarUser -ErrorAction Stop
$StartDateTime = (Get-Date).ToString("yyyy-MM-dd")
$EndDateTime = (Get-Date).AddDays(42).ToString("yyyy-MM-dd")

[array]$Events = Get-MgUserCalendarView -UserId $CalendarUser -CalendarId $Calendar.Id -All -EndDateTime $EndDateTime -StartDateTime $StartDateTime -ErrorAction Stop

After finding the corporate events, the script creates an ICS file for each event and adds the file as an attachment that is later sent to users. The code to create and send a mail message with attachments using the Send-MgUserMail cmdlet is well-known ground that I don’t cover here.

Problems Appear

At first glance, everything seemed good. The script created and sent the welcome messages to the targeted recipients complete with ICS files for the corporate events (Figure 1).

A welcome message with attached .ICS files.
Figure 1: A welcome message with attached .ICS files

But then reality hit. The ICS files looked good when examined in PowerShell and could be used to create events, but the events are devoid of any Teams online meeting information (Figure 2). We know when the event is arranged for and its subject, but the join URL is missing.

The not-very-good ICS file missing the information to allow the user to connect to a Teams online event.
Figure 2: The not-very-good ICS file missing the information to allow the user to connect to a Teams online event

Hmmm.. something was obviously wrong, but what could it be?

The Special Nature of Teams ICS Files

People use ICS files all the time to send details of Teams online meetings to other users. The difference between the hand-crafted files created by the script and those generated by Exchange Online when people export events as ICS files from the calendar is that Exchange adds special meeting semantics. Outlook clients recognize the semantics and trust the information contained in the file, which means that the clients are happy to display the full information from the ICS file and will allow users to add events to their calendars, complete with the online join information. To see what I mean, download an ICS file from Outlook and open it with Notepad.

A bunch of processing must be done to make sure that the event information fetched from the Graph is formatted correctly for inclusion in an ICS file. For example, lines must not exceed 75 characters, time zones must be handled so that the correct meeting times are used when the ICS is imported in the calendar, and so on. You can look at script code in the Office 365 IT Pros GitHub repository to see everything that had to be done. In the end, it all worked and Outlook imported the ICS attachments to create new events (Figure 3). Even better, the Teams join information is preserved and can be used to connect to the online spaces.

A properly formatted ICS can be imported into a user calendar by Outlook.
Figure 3: A properly formatted ICS can be imported into a user calendar by Outlook

By comparison, the ICS files generated by Zoom are simple calendar items that happen to include a join URL in the body. Although Zoom has an Outlook add-in to manage scheduling within Outlook, Zoom doesn’t operate inside an ecosystem like Microsoft 365, so its ICS files don’t need to contain the special information Microsoft uses to recognize a Teams online meeting.

After all that, I should say that this is not the solution I would use. Instead of sending ICS files around, a better approach is to add the new users to the participant list for the events. The new users will receive invitations to the events and can RSVP as they choose. Creating the code to add extra people to an event’s participant list is much easier, as I will explore in a future article.


Need help to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.