PowerShell: Search for appointments

You can use the Exchange cmdlet Search-Mailbox for a wide variety of searches, but it does have some limitations.  Attached here is a PowerShell script that uses EWS to search appointments, and as the script uses both server and client side search it can be used to performs searches that aren't possible with Search-Mailbox (and it can also be used on your own mailbox without requiring to connect to an Exchange PowerShell session).  This script will work for both on-premise installations of Exchange (2010 or higher, it will probably work with 2007 but may need minor changes, and I haven't tested there!) as well as Office 365.

The download link for this script has been removed, as it has now been moved to https://code.msdn.microsoft.com/exchange/PowerShellEWS-Search-e0f9c169




 -Mailbox The mailbox to be processed (if missing, current user's mailbox is assumed, though this will only work in a domain environment).
 -FolderPath  Folder to search - if omitted, the mailbox calendar folder is assumed.
 -PublicFolders  If this switch is present, folder path is required and the path points to a public folder.
 -Subject  Subject of the appointment(s) being searched.  A -like comparison is used, so wildcards should work.
 -StartsAfter  Start date for the appointment(s) must be after this date.
 -StartsBefore  Start date for the appointment(s) must be before this date.
 -EndsBefore  End date of the appointment(s) must be before this date.
 -EndsAfter  End date of the appointment(s) must be after this date.
 -CreatedBefore  Only appointments created before the given date will be returned.
 -CreatedAfter  Only appointments created after the given date will be returned.
 -LastOccurrenceBefore  Only recurring appointments with a last occurrence date before the given date will be returned.
 -LastOccurrenceAfter  Only recurring appointments with a last occurrence date after the given date will be returned.
 -IsRecurring  If this switch is present, only recurring appointments are returned (note that this is implied if LastOccurrenceBefore or LastOccurrenceAfter are set).
 -Delete  If specified, any matched appointments will be deleted. Use with care!
 -Credentials  Credentials used to authenticate with EWS.
 -Username  Username used to authenticate with EWS.
 -Password  Password used to authenticate with EWS.
 -Domain  Domain used to authenticate with EWS.
 -Impersonate  Whether we are using impersonation to access the mailbox.
 -EwsUrl  EWS Url (if omitted, then autodiscover is used).
 -EwsManagedApiPath  Path to managed API (if omitted, a search of standard paths is performed).
 -IgnoreSSLCertificate  Whether to ignore any SSL errors (e.g. invalid certificate) - use with care.
 -AllowInsecureRedirection  Whether to allow insecure redirects when performing autodiscover.
 -LogFile  Log file - activity is logged to this file if specified.
 -ExportCSV  File to which matching appointment data will be exported




.\Search-Appointments.ps1 -LastOccurrenceAfter 30/Dec/2016 -ExportCSV c:\temp\apts.csv
Will find all appointments in the current user's calendar with a last occurrence date after 30th December 2016 (note that this will only match recurring appointments)

.\Search-Appointments.ps1 -Mailbox user@office365.com -Credentials (Get-Credential) -LastOccurrenceAfter 30/Dec/2016 -ExportCSV c:\temp\apts.csv
Performs the same search as the first example, but against an Office 365 mailbox and will prompt for credentials.

.\Search-Appointments.ps1 -LastOccurrenceAfter 30/Dec/2016 -ExportCSV c:\temp\apts.csv -Delete
Performs the same search as the first example, but deletes any matching appointments found (the details of those appointments will also be exported to the CSV).

Comments (15)

  1. Jeff says:

    Looking through the script, and I'm no powershell/scripting expert by any means…but is there a way that we can get this script to crawl through all mailboxes that are conference rooms (where RecipientTypeDetails -eq "RoomMailbox")?

    Our objective is to be able to purge meetings that are on conference room mailboxes after a user leaves our agency, and thus freeing up that time that they might have booked.

  2. Mikael says:

    Hi, I have a customer where we try to find and delete old, scripted and misplaced appointments from a local Exchange 2010 environment. When running the script with the following one-liner:

    Get-Mailbox -ResultSize Unlimited | select PrimarySmtpAddress | foreach {.Search-Appointments.ps1 -Mailbox $_ -Subject "Långfredagen" -StartsAfter 24/Apr/2016 -EndsBefore 27/Apr/2016 -ExportCSV C:tempResult.csv}

    We receive this error:

    Exception calling ”Bind” with ”2” argument(s): ”The Url Property on the ExchangeService object must be set.”

    At line:1 char:75

    + Get-Mailbox -Resultsize Unlimited | select PrimarySmtpAddress | foreach { .Sear …


         + CategoryInfo                : NotSpecified: (:) [Search-Appointments.ps1], MethodInvocationException

         + FullyQualifiedErrorId  : ServiceLocalException,Search-Appointments.ps1

    Any suggestions on WHY this occurs? Are we using this script the wrong way?

  3. If you receive an error that the Url property must be set, it means that autodiscover failed for that mailbox and a Url couldn't be determined.  Easy way round this is to specify the EWS Url using -ewsurl parameter.  The correct way to resolve it is to fix autodiscover. 🙂

    Jeff… I know this is months too late, but Mikael's example shows how you could run this script against room mailboxes (just modify the Get-Mailbox call appropriately).

  4. Mikael says:

    Thanks David,

    As I expected. I will let them know that we need insight in their environment to help them configure Autodiscover correctly.

  5. Clint says:

    Is there a way to get the body of the Appointment?

    I’m not sure what to modify to get the “body” or “Message” of the Calendar event.
    Could someone assist?

  6. Harinder Kaur says:

    $itemId = New-Object Microsoft.Exchange.WebServices.Data.ItemId(“xx”)

    What does the above-command do… whats “xx”? The script runs without errors, even finds the content I am looking for, but doesn’t delete coz $deleteIds.Count is blank.

  7. Doug Thompson says:

    this works great…against my own mailbox.

    I am getting “The specified folder could not be found in the store.” when I try and run the script against another user’s mailbox – I am using my “Global Admin” credentials for authentication.

    Any thoughts?

    1. Doug Thompson says:

      maybe I should have read everything before posting…I was missing the “-impersonate” flag.

      awesome script! just made me look godlike to my CTO 🙂

  8. David B says:

    Great script. I was wondering if there is a way to add a line to find any recurring appointments that don’t have an End date. Those are the ones that I want to delete. Thank you.

  9. Jeremy says:

    Thank you so much for this script, it’s a huge help! Just wondering if there is a way to detect an instance of a recurring event. For example, if I want to see all events occurring tomorrow, and there are two “normal” events and one instance of a recurring event, I can only see the 2 “normal” events. I was hoping to get around this by using the -IsRecurring switch, but that only appears to pick up the master event, not the individual instances.

  10. Johannes says:

    Thanks a lot! Great script!. For those of you who’re using Outlook365, the EwsUrl is the following: https://outlook.office365.com/EWS/Exchange.asmx.
    I didn’t know that and searched for a while.

  11. Mark says:

    Hi, when I runt he script with one of the examples, I get this error.

    New-Object : Cannot find type [System.Collections.Generic.Dictionary“2[System.String,System.Object]]: make sure the as
    sembly containing this type is loaded.
    At C:\Windows\system32\Search-Appointments.ps1:665 char:39
    + $script:itemsToDelete = New-Object <<<< 'System.Collections.Generic.Dictionary“2[System.String,System.Object]'
    + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

    Contains : Method invocation failed because [System.Object[]] doesn't contain a method named 'Contains'.
    At C:\Windows\system32\Search-Appointments.ps1:334 char:38
    + if ( $script:loadedItems.Contains <<<< ( $item.Id.UniqueId ) )
    + CategoryInfo : InvalidOperation: (Contains:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

  12. Very Nice Script, anything in this class Microsoft.Exchange.WebServices.Data.EmailMessageSchema is not getting any value.
    I am able to get values if I add more fields to the script for example: Microsoft.Exchange.WebServices.Data.ItemSchema.
    Troubleshooting, if found the resolution will post ..

    Sukhija Vikas

    1. I think it may be because of appointments, I have used different property to get the info I need 🙂

      Sukhija Vikas

  13. Suresh T says:

    HI David,

    I am looking on how to get the required attendees details in this script. How do i get that.

Skip to main content