I have a bunch of monitoring scripts that send me e-mail notifications using the Send-MailMessage cmdlet. This has worked well for a while because it reminds me that I need to check on something. The problem I’ve had lately is that I have tons of e-mail every morning, and often times I’ll mark these messages as read, and then forget about them later in the day. So one of the things I’ve been doing lately is adding these notifications as calendar items in my mailbox. This has worked even better because I can set the start date later in the day and then Outlook and my mobile phone will remind me when I need to check on something.

This is the PowerShell function I wrote that uses the EWS Managed API to create calendar items in an Exchange mailbox:

function New-CalendarItem {
        [Parameter(Position=1, Mandatory=$true)]
        [Parameter(Position=2, Mandatory=$true)]
        [Parameter(Position=3, Mandatory=$true)]
        [Parameter(Position=4, Mandatory=$true)]
        [Parameter(Position=5, Mandatory=$false)]
        [Parameter(Position=6, Mandatory=$false)]
        [Parameter(Position=7, Mandatory=$false)]
        [Parameter(Position=8, Mandatory=$false)]

    Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"
    $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
    $user = [ADSI]"LDAP://<SID=$sid>"        
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    if($Impersonate) {
        $ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Impersonate 
        $service.ImpersonatedUserId = $ImpersonatedUserId   

    $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service
    $appointment.Subject = $Subject
    $appointment.Body = $Body
    $appointment.Start = $Start
    $appointment.End = $End 
    if($RequiredAttendees) {$RequiredAttendees | %{[void]$appointment.RequiredAttendees.Add($_)}}
    if($OptionalAttendees) {$OptionalAttendees | %{[void]$appointment.RequiredAttendees.Add($_)}}
    if($Location) {$appointment.Location = $Location}

After you’ve added the function to your PowerShell session, creating a calendar item is easy. For example, this will create a calendar item that starts in six hours:

$start = (Get-Date).AddHours(6)
$end = $start.AddHours(1)

New-CalendarItem -Subject "Check Disk Free Space" -Body "Make sure that servers are not running out of disk space" -RequiredAttendees helpdesk@contoso.com -Start $start -End $end

In this example, I’ve used the optional parameter RequiredAttendees to add the helpdesk mailbox as an attendee to the appointment. You can also use this function with the Impersonate parameter to create calendar items in another users mailbox. You’ll need to be assigned the application impersonation RBAC role in order for this to work:

New-CalendarItem -Subject "Reboot Server" -Body "Reboot EXCH-SRV01 server after 5PM today" -Start $start -End $end -Impersonate sysadmin@contoso.com

If you want to create calendar items in multiple mailboxes, loop through a collection with the foreach-object cmdlet and run the function for each user. For example, to create an appointment in the mailbox of each member of the ITSupport distribution group:

Get-DistributionGroupMember ITSupport | Foreach-Object{New-CalendarItem -Subject "Install Hotfixes" -Body "Start patching servers after 5PM today" -Start $start -End $end -Impersonate $_.PrimarySMTPAddress}

I’ve actually found this to be quite useful. I’ve implemented some basic functionality here, but you can extend this function to do even more. Check out the available members in the Appointment class for more details.