Partager via


Delete a list of folders from specific mailboxes with EWS Managed API 2.2 and Application Impersonation, in Exchange Online

Some time ago, I worked with a customer that wanted to remove a folder that he had created in multiple mailboxes and he didn't know how to achieve that in bulk.

At that moment, I came across a very interesting article written by one of our colleagues that explains how to create a folder under Inbox, for multiple mailboxes:  https://blogs.msdn.microsoft.com/akashb/2011/07/22/creating-folder-using-ews-managed-api-1-1-from-powershell/

Using that article  and some valued hints got from the owner then, I got a perspective about how a similar script would look like when it comes to folder removal.

Thus, after investing some time in testing and research, I have eventually managed to create a script that deletes a list of folders from specific mailboxes, by using EWS Managed API and Application Impersonation.

 

Prerequisites:

-Create a new RBAC group from Exchange Admin Center - Permissions - Admin Roles. Add the 'ApplicationImpersonation' role to the group and add as member, the service account that will connect to the mailboxes from UserAccounts.txt with Impersonation (it can be your Global Admin account)

-Create a log file under C:\Temp\Log.txt.

-Create a file with the folder names: "Folders.txt"

FolderName
test1
test2
test3

-Create a file with the user mailboxes: "UserAccounts.txt"

WindowsEmailAddress
user1@domain.onmicrosoft.com
user2@domain.onmicrosoft.com
user3@domain.onmicrosoft.com

 

 DISCLAIMER: This application is a sample application. The sample is provided "as is" without warranty of any kind. Microsoft further disclaims all implied warranties including without limitation any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the samples remains with you. in no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss arising out of the use of or inability to use the samples, even if Microsoft has been advised of the possibility of such damages). Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.



# The script requires EWS Managed API 2.2, which can be downloaded here:
 https://www.microsoft.com/en-gb/download/details.aspx?id=42951
# Make sure the Import-Module command matches the Microsoft.Exchange.WebServices.dll location of EWS Managed API, chosen during the installation



 [string]$info = "White"                              # Color for informational messages
 [string]$warning = "Yellow"                          # Color for warning messages
 [string]$error = "Red"                               # Color for error messages
 [string]$LogFile = "C:\Temp\Log.txt"                 # Path of the Log File



function DeleteFolder($MailboxName) 
 { 
Write-host "Searching for folder in Mailbox Name:" $MailboxName -foregroundcolor  $info
Add-Content $LogFile ("Searching for folder in Mailbox Name:" + $MailboxName) 

 #Change the user to impersonate
 $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName) 

do
 { 

 $oFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) 

 $oFolderView.Traversal = [Microsoft.Exchange.Webservices.Data.FolderTraversal]::Deep

 $oSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$FolderName) 

 $oFindFolderResults = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$oSearchFilter,$oFolderView) 

if (!$oFindFolderResults.TotalCount) 
 { 
Write-host "Folder does not exist in Mailbox:" $MailboxName -foregroundcolor  $warning
Add-Content $LogFile ("Folder does not exist in Mailbox:" + $MailboxName) 
 } 
  else
 { 
Write-host "Folder EXISTS in Mailbox:" $MailboxName -foregroundcolor  $warning
Add-Content $LogFile ("Folder EXISTS in Mailbox:" + $MailboxName) 

 $oFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$oFindFolderResults.Folders[0].Id) 

Write-host "Deleting Folder:" $FolderName -foregroundcolor  $warning
Add-Content $LogFile ("Deleting Folder:" + $FolderName) 

 $oFolder.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete) 
   } 

 } while($oFindFolderResults.TotalCount) 

 $service.ImpersonatedUserId = $null

 } 
Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" 

 $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList Exchange2013_SP1

  #Provide the credentials of the O365 account that has impersonation rights on the mailboxes declared in UserAccounts.txt
 $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList  "user@domain.onmicrosoft.com","Password"  

 #Exchange Online URL
 $service.Url= new-object Uri("https://outlook.office365.com/EWS/Exchange.asmx") 

 #Read the data
import-csv "C:\Users\youruser\Desktop\Application Impersonation\Folders.txt" | foreach-object { 
 $FolderName = $_.FolderName.ToString() 

import-csv "C:\Users\youruser\Desktop\Application Impersonation\UserAccounts.txt" | foreach-object { 
 $WindowsEmailAddress = $_.WindowsEmailAddress.ToString() 

 #Catch the errors
trap [System.Exception] 
 { 
Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error
Add-Content $LogFile ("Error: " + $_.Exception.Message) 
continue
 } 
DeleteFolder($WindowsEmailAddress) 
 } 
   } 

 

Note: In case you want to create a set of given subfolders under the Inbox or another WellKnownFolderName, you can use the script from the MSDN Blog, with the below considerations:

-Use the new location for EWS Managed API 2.2:  Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"

-Enter the name of the folder where you want to save the new subfolders:
$oFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)

-In the last part of the script, when you read the data, you have to reverse the folder reading with the user accounts reading:
#Read the data
import-csv "C:\Users\a-castre\Desktop\Application Impersonation Blog\UserAccounts.txt" | foreach-object {
$WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

import-csv "C:\Users\a-castre\Desktop\Application Impersonation Blog\Foldersbulk.txt" | foreach-object {
$FolderName = $_.FolderName.ToString()

Comments

  • Anonymous
    May 04, 2017
    This worked exactly as stated and saved me a bunch of time - thank you!
    • Anonymous
      May 05, 2017
      Glad to hear that Jim. You're welcome!:)
  • Anonymous
    August 17, 2017
    This script worked beautifully! When we moved to Office 365 we stopped using a mailbox archiving product which left behind a redirection folder in everyone's mailbox. We were getting help desk calls asking why they could no longer access the old product via that leftover folder. This script deleted all those folders for us. Thank you so much!
    • Anonymous
      August 17, 2017
      Hey Chris,I am glad to see that the script was useful and that it saved you from additional labor:)Catalin
  • Anonymous
    September 12, 2017
    Greetings! I'm attempting to use this to clean up a slew of useless folders after a migration, but it's continually generating a 401 Unauthorized error. I've confirmed I can impersonate a mailbox within Outlook, so it would seem permissions are correct. Any ideas?
    • Anonymous
      September 12, 2017
      Hi Nicholas,You forgot to provide your credentials within the script code.Catalin
      • Anonymous
        September 12, 2017
        The comment has been removed
        • Anonymous
          September 12, 2017
          This error is normally received when the credentials entered in the "Provide credentials" section are not correct and thus, the connection to the service is not made. Other from that, I don't see any reason why you'd get it. Try with another mailbox to see how it goes.
      • Anonymous
        September 12, 2017
        I got it figured out. Your script worked like a charm. Thanks!
        • Anonymous
          September 12, 2017
          Super! You are welcome.
  • Anonymous
    October 25, 2017
    Hello,I have the error: Error: The request failed. Errore del server remoto: (404) Non trovato.
    • Anonymous
      October 26, 2017
      Hi Alberto,I think you forgot to enter the credentials:#Provide the credentials of the O365 account that has impersonation rights on the mailboxes declared in UserAccounts.txt$service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList "user@domain.onmicrosoft.com","Password"Cheers,Catalin
  • Anonymous
    October 31, 2017
    Amazing Work Catalin!!! Works like charm!
    • Anonymous
      November 01, 2017
      Thanks Saad! Glad that it was useful!Cata