A Script To Delete ALL Of The Attachments In A Single Exchange Server Mailbox (use with care!)

[ 23 ] Comments
Share

I recently wrote an article describing how to delete all of the attachments from all of the mailboxes on an Exchange Server. Today, it occurred to me that it would probably be more useful to remove all the attachments from a single mailbox. So, based on that other article, here is another script that will do just that.

Save all the text below to a .ps1 file, and run it as a script in PowerShell.  Don’t come crying to me if you do this without CAREFULLY THINKING ABOUT IT FIRST!  If you’re interested in trying it, let me know.  There are a couple of assumptions made that you may not be able to work out for yourself. By the way, the script contains a sample mailbox name ‘yourUserName’. Please replace that with the actual mailbox name that you are interested in.

Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"
$credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials(ADMINUSERNAME, PASSWORD)
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
$service.Credentials = $credentials
$service.Url = "https://localhost/ews/exchange.asmx"
$mailbox = Get-Mailbox yourUserName
$folderFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter
$folderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$folderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$itemFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::HasAttachments, $true)
$itemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(10000)
write-host $mailbox.displayName
$folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot, $mailbox.primarySmtpAddress.ToString())
$folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId)
$folders = $service.FindFolders($folderId, $folderFilter, $folderView)
foreach ($subFolder in $folders.Folders)
{
write-host $subFolder.displayName
$items = $subFolder.FindItems($itemFilter, $itemView)
foreach ($item in $items.Items)
{
write-host $item.subject
$item.Load()
foreach($attachment in $item.Attachments)
{
write-host $attachment.Name
$item.Attachments.Remove($attachment)
break
}
$item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AutoResolve)
}
}

23 Responses to A Script To Delete ALL Of The Attachments In A Single Exchange Server Mailbox (use with care!)

  1. Ryan says:

    Hello,

    I’ve been attempting to run this script and have run into some issues when trying to run it. I downloaded web services 2.0 rather than 1.1, not sure if that would make a difference.

    You mentioned that you made some assumptions and I was curious if you could help walk me through the process.

    Thanks for the help.

    • admin says:

      What issues are you having? Looking at the script, I think the only assumptions are that the EWS dll is in that particular location (if you have v2, then that is probably incorrect), and that the default web site responds to the name ‘localhost’ (which is usually a safe assumption).

      So, if your web services dll is not here:
      C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll
      you will need to change that bit of the script.

  2. Ryan says:

    Thanks for the quick response.

    I did change the file path for the .dll to match the file path using 2.0 version and it appears that the script is able to import the .dll (at least its not throwing a error message).

    the first error message I am receiving is “The term ‘ADMINUSERNAME’ is not recognized as the name of a cmdlet” for the credentials variable. I have tried leaving adminusername and password as is and changing the adminusername and password to match my domain admin’s credentials. In both cases I receive the same error message. Do you know what the correct format for this segment?

    I have also changed the “your user name” with the mailbox I would like to remove the attachments for.

    Thanks again for the help.

    • admin says:

      I think I know what the problem is. It’s not obvious from the script, but the ADMINUSERNAME and PASSWORD values that you use (your own, of course) should be in quotes. I don’t usually include them in placeholder variables, because it makes it look like those are the actual values that you are supposed to use.

      • Ryan says:

        Hey thanks again for the help, you were correct and the “s did allow me to enter my credentials for this script.

        However the script was still unable to run, I got a couple of error messages:
        Cannot find an appropriate constructor for type microsoft.Exchange.webservices.datasearchfilter
        and
        exception calling “bind” with 2 argument(s) “The Request failed. The remote server returned an error: (403) forbidden.
        I also received the same error message for “findfolders”

        I tried finding 1.1 version and was unable to find it. I was able to find 1.2.1 though that version did get me any further than version 2.0.

        Thanks again for the help.

        • admin says:

          Can you find the requests for /EWS/Exchange.asmx in your iis log file (they should be generated by the bind operation) with the number 403 near the end? If so, can you post one here?

          • Ryan says:

            The only records that I found that matched your specifications were:
            2013-09-06 15:32:09 ::1 POST /ews/exchange.asmx – 80 – ::1 ExchangeServicesClient/15.00.0516.014 403 4 5 0
            2013-09-06 15:32:09 ::1 POST /ews/exchange.asmx – 80 – ::1 ExchangeServicesClient/15.00.0516.014 403 4 5 0

            These records always appear in a pair as shown above.

            Thanks again and let me know if you need any additional information.

          • admin says:

            Ah, you’re getting a 403.4 response, which means ‘SSL Required’. That means, of course, that the line

            service.Url = “http://localhost/ews/exchange.asmx”

            should read

            service.Url = “https://localhost/ews/exchange.asmx”

            I’ll make a small change to the article.

  3. Jerry Brower says:

    Could this script be modified to delete only attachments in the “sent items” folder?
    What about deleting attachments only from messages older than x days?

    I have always thought it was silly to keep the attachments in the sent items, since you already have the file. (you attached it) That alone would save me over 25% space.

    Thanks

  4. Jerry Brower says:

    Is it possible to modify this script to only delete attachments for messages in the Sent Items folder?
    What about only from messages older than x days?
    These 2 possibilities would make this script awesome. I have estimated that it would save me more than 25% storage space.

    • admin says:

      Yes, I think that would be possible. I’ll have a look over the next few days.

    • admin says:

      Okay, I managed to find the time to do it. It’s a bit different, because you have to go directly to the Sent Items folder and recurse it, rather than just starting at the mailbox root, and getting all the folders. Also, not that this time there is a different path to the EWS dll (I have EWS API v2 now). This version shouldn’t actually delete anything, but just show attachments in items older than 14 days. It will give you a chance to test it first.

      Not sure how this will look posted as a comment, but here goes. If you can’t copy it successfully, I’ll write it up on a new page.

      Import-Module -Name “C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll”

      function doFolder($inFolder)
      {
      write-host “Folder:” $inFolder.displayName
      $items = $inFolder.FindItems($itemFilter, $itemView)
      foreach ($item in $items.Items)
      {
      write-host $item.Subject
      if ((New-TimeSpan (Get-Date) $item.DateTimeReceived).Days -lt -14)
      {
      $item.Load()
      foreach($attachment in $item.Attachments)
      {
      write-host $attachment.Name
      #$item.Attachments.Remove($attachment)
      break
      }
      $item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AutoResolve)
      }
      }
      $folders = $service.FindFolders($inFolder.Id, $folderView)
      foreach ($folder in $folders.Folders)
      {
      doFolder($folder)
      }
      }

      $credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials(YOURUSERNAME, YOURPASSWORD)
      $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010)
      $service.Credentials = $credentials
      $service.Url = “https://localhost/ews/exchange.asmx”
      $mailbox = Get-Mailbox “YOURMAILBOXNAME”
      $folderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
      $folderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Shallow
      $itemFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::HasAttachments, $true)
      $itemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(10000)
      write-host $mailbox.displayName
      $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems, $mailbox.primarySmtpAddress.ToString())
      $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId)
      doFolder($folder)

  5. Mahmoud Badran says:

    Would that work on Office365 ? exchange online?

    • admin says:

      I’ve no experience of Exchange online. Do you get admin access to the Exchange Shell? If so, then it might work.

  6. Andrew James says:

    This looks really neat. I’ve got a requirement to delete attachments on calendar items only.

    Instead of the sent items can the script be pointed at the calendar folder instead? Thanks.

    • admin says:

      Yes. In the line

      $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot, $mailbox.primarySmtpAddress.ToString())

      change MsgFolderRoot to Calendar .

  7. alan says:

    when I ran $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId)
    I got below error. What could be wrong?

    PS C:\> $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId)
    Exception calling “Bind” with “2″ argument(s): “The specified object was not found in the store.”
    At line:1 char:1
    + $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServiceResponseException

  8. alan says:

    when I run $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId), I got below error. what could be wrong?

    Exception calling “Bind” with “2″ argument(s): “The specified object was not found in the store.”
    At line:1 char:1
    + $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServiceResponseException

    • admin says:

      Are you typing in the script lines one at a time? The whole thing is meant to be run as a script file. Or are you just typing in individual lines to learn about EWS?

      Anyway, it looks like the $folderId variable may be empty. A valid folder name is supposed to be passed to the script as a command line parameter, otherwise it won’t be able to locate it. If you’re typing in that single line by itself, it won’t know which folder you’re looking for.

  9. alan says:

    I ran it in Powershell_ISE and debug it line by line from beginning. The $folderID show SentItem (see below). The $service is not empty either. but once it reached line “$folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId)”, it throws an error.

    [DBG]: PS C:\Users\alan>> $folderId

    FolderName Mailbox UniqueId ChangeKey
    ———- ——- ——– ———
    SentItems testalan@stone.com

    • admin says:

      Oh, right. I haven’t tried it in the ISE. In fact, it isn’t even installed on my server. But it shouldn’t be starting at SentItems like that. It’s supposed to start at MsgFolderRoot. Does your code still have MsgFolderRoot in? Or did you change it so that you are just working in SentItems? I don’t actually think that should be a problem even if you are, but I want to try and duplicate exactly what you are seeing.

  10. alan says:

    I copied exactly the code you have here as you posted in above section:
    admin
    January 27, 2014 at 11:53 am

    I run it in both powershell and ISE, I got same error at line 38.
    $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId)

    Your code doesn’t have MsgFolderRoot in this version. I replaced SentItems with MsgFolderRoot and got same error.

    • admin says:

      There is this line in the code

      $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot, $mailbox.primarySmtpAddress.ToString())

      Maybe it didn’t all get copied? If you can send me your copy of the script to email@leederbyshire.com , I’ll see if I can run it okay here.




Leave a Reply to Jerry Brower Cancel reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>