Category Archives: Exchange Online

Using Exchange Web Service with Powershell to remove calendar appointments without notification

While helping out a client recently something in their migration went wrong, I ran into an interesting challenge. Calendars of users were merged, many many times. The resulting duplicates that shouldn’t be in user’s calendars could be identified easily: the mailbox was neither the organiser nor invited to these calendar events.

But how to remove them? Graph won’t allow you to do so without notifying the recipients (leading, potentially, to thousands of confused users).

Luckily, EWS DOES allow us to do so, and if you ever need to work with EWS (Office 365 Exchange Online) using Powershell, this code sample could come in handy 🙂

Add-Type -Path "C:\Users\jos\Desktop\net35\Microsoft.Exchange.WebServices.dll"

$Service = [Microsoft.Exchange.WebServices.Data.ExchangeService]::new()
$Service.Credentials = [System.Net.NetworkCredential]::new("admin@onedrivemapper.onmicrosoft.com" , "yourpassword")
$Service.Url = "https://outlook.office365.com/EWS/Exchange.asmx"

$maxDaysIntoTheFuture = 365

function Remove-ObsoleteCalendarItems{
    Param(
        $primaryEmailAddress #eg: admin@onedrivemapper.onmicrosoft.com
    )

    $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$primaryEmailAddress)   
    $Calendar = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service,$folderid)
    $Recurring = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Appointment, 0x8223,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Boolean); 
    $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)  
    $psPropset.Add($Recurring)
    $psPropset.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text;

    #Define Date to Query 
    $currentDay = 0
    while($True){
        $StartDate = (Get-Date).AddDays($currentDay)
        $EndDate = $StartDate.AddDays(14)  
        $currentDay += 14

        if($currentDay -gt $maxDaysIntoTheFuture){
            break
        }

        $CalendarView = New-Object Microsoft.Exchange.WebServices.Data.CalendarView($StartDate,$EndDate,1000)    
        $fiItems = $service.FindAppointments($Calendar.Id,$CalendarView)
        if($fiItems.Items.Count -gt 0){
            $type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
            $type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.Item" -as "Type")
            $ItemColl = [Activator]::CreateInstance($type)
            foreach($Item in $fiItems.Items){
                $ItemColl.Add($Item)
            } 
            [Void]$service.LoadPropertiesForItems($ItemColl,$psPropset)  
        }

        foreach($Item in $fiItems.Items){  
            if($Item.Organizer.Address -ne $primaryEmailAddress -and $Item.RequiredAttendees.Address -notcontains $primaryEmailAddress -and $Item.OptionalAttendees.Address -notcontains $primaryEmailAddress){
                $Item.RequiredAttendees.Clear() #this also works if no one is invited
                $Item.OptionalAttendees.Clear() #this also works if no one is invited
                $Item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite,[Microsoft.Exchange.WebServices.Data.SendInvitationsOrCancellationsMode]::SendToNone)
                $Item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::MoveToDeletedItems)
                write-host "deleted item $($Item.Subject) without notifying recipients"
            }
        }
    }
}

The required EWS DLL’s can also be found in my git repository:

More licenses and features

Pivot table of all Microsoft cloud suites and their features

I’ve updated the Microsoft cloud suites feature comparison page with all other suites Microsoft including all their features. I’ve also added all Education sku’s. You can use the pivot table to sort / mix / match according to your exact needs. If you need any assistance with Microsoft 365, don’t be a stranger 🙂

Name must be unique per owning mailbox. There’s already a request with the name

While migrating some public folders to Office 365 Groups, I kept running into issues with one of the target groups:

“De gebruiker XXX heeft al een aanvraag die in behandeling is. Verwijder de bestaande aanvraag en hervat de huidige batch of start een nieuwe batch voor deze gebruiker. –> Name must be unique per owning mailbox. T”

In english you’ll probably see “Name must be unique per owning mailbox. There’s already a request with the name “.

I figured there was a moverequest hanging / not properly cleaned up; but none to be found with get-moverequest, get-migrationuser or get-migrationbatch; all clean!

In the end, it took almost 2 weeks of patience after contacting support until the Exchange Online backend team reset a hanging job on their end. So if you google above errors and come here, check if you have double jobs, if you don’t, request support and make sure they escalate to the product team immediately.