The PowerShell script described in this post allows you to automatically archive email attachments like automated reports from an Exchange mailbox to a file share.
Latest posts by Andrew Jacops (see all)

In my environment I have several applications that automatically send daily, weekly, monthly, etc. reports as attachments to a mailbox where they are extracted programmatically and moved to a file share. Initially, these reports were coming to everyone in the system administrators group clogging inboxes and filling space quotas. Literally hundreds of rules were set up, folders and subfolders created, and then the emails would be purged manually when they no longer served their purpose.

Setting up this automation has helped me to keep a clean inbox and organize all of these reports onto a central location that everyone can access. It also has helped with any compliance requirements, such as with PCI (Payment Card Industry). Putting these reports in a single location makes it easy to back up to tape for offsite storage.

In this post, I’ll show you how to easily create this same functionality so you too can easily manage the insane amount of automated reports we as administrators are constantly being bombarded with.

Auto-archive overview ^

In the figure below, I have laid out a general overview of what this process will accomplish.

Archive email attachments with PowwerShell

Archive email attachments with PowwerShell

 

  1. The automated reports are created and sent via email to the Exchange server.
  2. The emails are then pulled out of the mailbox via the script we will create.
  3. The reports are stripped from the emails and the emails are deleted.
  4. All of the reports are then stored on an easily accessible file server.

Setting up the Exchange mailbox ^

Undoubtedly the first thing we need to accomplish is to create a mailbox where these files can be sent for processing.

Create the Exchange mailbox

Create the Exchange mailbox

There is nothing special about the account that needs to be created. Here I have created something super simple that would easily be remembered. Of course we need know the email address and password for our script so set those aside.

Note: I will be using the monthly.reporting user account associated with this mailbox to run the scheduled task. If you want to use yours or another account, you must give them privileges to this mailbox.

The PowerShell script ^

Here is the script in its entirety.

Breaking the script down ^

First we’ll instantiate and assign values to our main variables.

The third variable is a little tricky. Because we’re using PowerShell, we need to load a library in order to connect to Microsoft Exchange. For simplicity, and because I know this script will require that an Exchange server be up and running, I just set the script up to run on one of my Exchange servers. This way I know that the DLL will be available. Otherwise, you can download the API and install it on your own PC.

Instantiate and load the DLL.

Now we will create the service object.

Again, I will be using the monthly.reporting account to run this scheduled task. That’s what the task will impersonate as.

The auto discovery URL is something that Exchange uses to auto configure clients such as Outlook Anywhere. Our script is using that API, so we’ll need to find that too.

Get the mailbox name so we can pull from it:

This part of the script will go through all of the messages in the inbox, ensure that it has attachments, and create a list of these messages.

The script will then loop through the list of messages that has attachments and load them for processing.

Each attachment will then be evaluated. They will be moved to the location we specified in the $downloadDirectory variable and append a date to the name.

Finally, we will mark the message as being read and, optionally, delete the message so it won’t be pulled by the script again.

Save your script as a PowerShell script somewhere you will remember. I usually put mine under something like C:\scripts\.

Setting up the Scheduled Task ^

Now that we have our script complete and ready to rock, we’ll need to set up the scheduled task that will trigger it to pull the attachments.

  1. Open your Task Scheduler by clicking the Start button, hovering over Administrative Tools, and selecting Task Scheduler.
    Open Task Scheduler
  2. When the Task Scheduler loads, click Create Basic Task… under the Actions menu in the far right.
    Create Basic Task
  3. When the wizard opens, give the task a descriptive name and click Next.
    Task name
  4. Select when you would like for this task to run and click Next.
    Schedule Task
  5. I selected Daily so the next dialogue box will ask for more specific information. Select your options and click Next.
    Daily task
  6. We are going to be running our script, but in an indirect way. We will first need to run the powershell.exe program and then pass our arguments to open the script we wrote to it. Select Start a program and click Next.
    Start a program
  7. Type powershell.exe into the Program/script: textbox. Then type "&'C:\scripts\auto-archive attachments.ps1'"Into the Add arguments (optional): textbox. This basically tells PowerShell that we want it to run this particular script. Leave the Start in (optional): box blank and click Next.
    PowerShell script
  8. This next modal box gives us a summary of our task. Click Finish when you’ve verified everything.
    Task summary

That’s it! You have created your scheduled task to run the script we created.

Summary ^

Congratulations! You have just creating a script and scheduled task that will make your administrative life much easier. As much as we all love paperwork, it is extremely time consuming and monotonous.

This scheduled task will now fire off daily, grab all of the attachments in the mailbox, move them to a network share, and delete the old messages from the inbox.

You can easily extend this and use it for other things. Here are some ideas:

  • Change it to move specific attachments to specific folders
  • Move attachments from a particular sender to a certain share
  • Set up another email to send files you want to save for yourself

The possibilities are endless and the ease at creating them definitely makes it worthwhile. Comment below with any thoughts you have for this script. Thanks for reading!

Join the 4sysops PowerShell group!

Your question was not answered? Ask in the forum!

0
Share
24 Comments
  1. Pretty cool post Andrew!

    0

  2. shaz 7 years ago

    what about execution policy of powershell?

    0

  3. Heiko 7 years ago

    Hi Andrew,
    this script is was i Need, but how can i create the filename with Sender Name and Subject, any ideas for me?
    Many thanks
    Heiko

    0

  4. Mohammed Shah Newaj 6 years ago

    Would you please re-write the script for Microsoft Exchange 2010 SP2 and Microsoft Exchange 2013 SP1.

    Thanks
    Mohammed Shah Newaj

    1+

  5. Author

    Mohammed,

    You just need to change the namespace on line 12 and ensure the $dllpath to "Microsoft.Exchange.WebServices.dll" is correct.

    Here is a list of the current namespaces that are supported: http://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.exchangeversion(v=exchg.80).aspx

    0

  6. John Bob 5 years ago

    Hi, great post and working really well in our Exch 2013 environment. What I cant work out though is a way of forwarding the email as well as carrying out this process. We have a need to forward it to another mailbox with the attachments but alter the subject. Any thoughts to this would be great.

    Thank you

    0

  7. John Bob 5 years ago

    Typical, after posting my last comment I got it working

    So after

    # Loop through the emails
    foreach ($miMailItems in $frFolderResult.Items){

    # Forward the email onto someone else
    $fwd = $miMailItems.CreateForward()
    $fwd.ToRecipients.Add("email@domain.com")
    $fwd.Subject = "Change subject but add original subject after this " + $miMailItems.Subject
    $fwd.Send()

    0

  8. Dustin 5 years ago

    This isn't working for me. I'm running the script as a scheduled task and it says it's completed with no errors, but nothing is being put in the share I created on my server.

    0

  9. Paul 5 years ago

    This looks close to what I have been trying to do. Is there a way to modify this to make it copy the email, not just the attachment?

    thanks.

    0

  10. Clint 5 years ago

    I am looking to use this script with a MS SQL Server agent job to automaticly extract attachments from a email address and then process them from the file share, however in order to get this working in my environment I need to alter the script to explicitly set the credentials instead of using the current user, what do I need to change to allow for this?

    0

  11. Andree 5 years ago

    Nice script

    I'm having some problems with the way iphone attaches inline images.

    data:image/png;base64,iVBORw0KGgo.....

    the Exchange server (2013) does not recognize that they have an attachment so the filter does not work.

    do you know of any way to get this to work?

    0

  12. Nick Sharma 4 years ago

    Will this work for Office 365? Sorry, i am new to .Net/MS world, so not sure what changes i would have to make for this to work in that environment. Thanks so much

    2+

  13. Dave Pidgeon 4 years ago

    Great post, got this up and running in no time at all. Thanks for sharing!

    0

  14. Jonathan Le 4 years ago

    Did anyone make it works with Office 365?

    1+

  15. Ramy 3 years ago

    Thank you for this post, but i have this error, any one can help me , i have Exchange 2013

    Exception lors de l'appel de «Bind» avec «2» argument(s): «L'objet spécifié est introuvable dans la banque d'informations.»
    Au caractère C:\Users\rfcrami\Desktop\Script To Save Attachement.ps1:23 : 1
    + $InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$fold ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServiceResponseException

    Impossible d’appeler une méthode dans une expression Null.
    Au caractère C:\Users\rfcrami\Desktop\Script To Save Attachement.ps1:32 : 1
    + $frFolderResult = $InboxFolder.FindItems($sfCollection,$view)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation : (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    0

  16. Przemek Wirkus 3 years ago

    Hi

    How to download the email as msg not the attachment?

    0

  17. Mike 2 years ago

    Hi, how would it be possible to just Export explicit file Patterns like *filename*.xlsx ?

     

    0

  18. Drew 2 years ago

    One thing I noticed about this script, when it goes to delete the email, because the mailbox of the "deleted Items" folder it is moving the email to is not specified, it defaults to moving it to the deleted items folder of whatever mailbox account is running the script, not the mailbox specified in $MailboxName.  So if you are looking for the email in your deleted items, check the mailbox of the account you're running the script as.

    0

  19. Craig Stephenson 1 year ago

    What changes would one need to make to the script to extract the attachments from the Online Archive Mailbox, rather than the main Mailbox?

    i.e. I have 2 mailboxes, in 2 different databases, one the main day-to-day Mailbox and the other the Online Archive. I only want to extract the attachments from the Online Archive, but leave the main Mailbox untouched.

    Would the -isarchive flag be used somewhere?

    0

  20. David 1 year ago

    We are using Exchange 2013 CU20, I'm getting the following errors when running.

    Exception calling "Bind" with "2" argument(s): "The specified object was not found in the store."
    At C:\scripts\saveattachments\SaveAttachment.ps1:24 char:1
    + $InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$fold ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServiceResponseException

    You cannot call a method on a null-valued expression.
    At C:\scripts\saveattachments\SaveAttachment.ps1:33 char:1
    + $frFolderResult = $InboxFolder.FindItems($sfCollection,$view)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException

    1+

    • @David

      Are you sure the mailbox name in line 2 of the script is correct?

      Also did you update the Exchange version of line 12?

      0

  21. Shawn 10 months ago

    Hi, I am using exchange 2010 sp3 and having trouble getting this working. I get the following errors below after updating line 12 with the correct exchange version.

    can you please help me get it working?

    Thank you!

    -----------------------------------------------------

    Exception calling "Bind" with "2" argument(s): "Exchange Server doesn't support the requested version."
    At C:\Scripts\New Text Document.ps1:24 char:66
    + $InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind <<<< ($service,$folderid)
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : DotNetMethodException

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:33 char:41
    + $frFolderResult = $InboxFolder.FindItems <<<< ($sfCollection,$view)
        + CategoryInfo          : InvalidOperation: (FindItems:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:39 char:19
    +     $miMailItems.Load <<<< ()
        + CategoryInfo          : InvalidOperation: (Load:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:45 char:15
    +         $attach.Load <<<< ()
        + CategoryInfo          : InvalidOperation: (Load:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:48 char:125
    +         $fiFile = new-object System.IO.FileStream(($downloadDirectory + "\" + (Get-Date).Millisecond + "_" + $attach.
    Name.ToString <<<< ()), [System.IO.FileMode]::Create)
        + CategoryInfo          : InvalidOperation: (ToString:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:49 char:16
    +         $fiFile.Write <<<< ($attach.Content, 0, $attach.Content.Length)
        + CategoryInfo          : InvalidOperation: (Write:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:50 char:16
    +         $fiFile.Close <<<< ()
        + CategoryInfo          : InvalidOperation: (Close:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    Property 'isread' cannot be found on this object; make sure it exists and is settable.
    At C:\Scripts\New Text Document.ps1:54 char:15
    +     $miMailItems. <<<< isread = $true
        + CategoryInfo          : InvalidOperation: (isread:String) [], RuntimeException
        + FullyQualifiedErrorId : PropertyNotFound

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:55 char:21
    +     $miMailItems.Update <<<< ([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
        + CategoryInfo          : InvalidOperation: (Update:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\New Text Document.ps1:58 char:25
    +     [VOID]$miMailItems.Move <<<< ("DeletedItems")
        + CategoryInfo          : InvalidOperation: (Move:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    0

  22. Kostia 10 months ago

    Hello. I have Exhange 2016 CU 12.

    1. i change $dllpath

    2. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)

    3. $sidbind = "LDAP://<SID=S-1-5-21-2014335331-2650893581-1730608386-1170" + $windowsIdentity.user.Value.ToString() + ">"

    4. 

    I dont understend, why error, help please:

    You cannot call a method on a null-valued expression.
    At C:\tmp\Attach.ps1:20 char:1
    + $service.AutodiscoverUrl($aceuser.mail.ToString())
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull
     
    Exception calling "Bind" with "2" argument(s): "The Url property on the ExchangeService object must be set."
    At C:\tmp\Attach.ps1:24 char:1
    + $InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($se ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : ServiceLocalException
     
    You cannot call a method on a null-valued expression.
    At C:\tmp\Attach.ps1:33 char:1
    + $frFolderResult = $InboxFolder.FindItems($sfCollection,$view)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    0

  23. Pavol Kutaj 2 weeks ago

    I have just used the script and it's working fine. I've had to create a mailbox for the service account and give it a full access to the target mailbox, also I put a correct version of the Exchange. Thanks a lot, much appreciated. 

    0

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2020

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account