none
How do I Save Outlook Attachments using Powershell? RRS feed

  • Question

  • I have seen a few examples of this for vbScript, but none for powershell.

    Here is a script I found to open Outlook and pick a folder:

    function Release-Ref ($ref) {
    ([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
    [System.__ComObject]$ref) -gt 0)
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
    }
    #-----------------------------------------------------
    $o = New-Object -comobject outlook.application
    $n = $o.GetNamespace("MAPI")

    $f = $n.PickFolder()

    Write-Host $f.name
     
    $a = Release-Ref($f)
    $a = Release-Ref($n)
    $a = Release-Ref($o)

    Now I want to save the Excel attachments in the folder I pick.

    Better yet, I want to Open the attachements in Excel and copy the contents to a master spreadsheet.

    Again, I found a vbScript in "Hey Scripting Guy" BUT, The format is so mangled, I can not read the script.

    Anyone else have that problem? The scripts come out as one long line of text.

    Monday, August 9, 2010 3:57 PM

Answers

  • While you were working on this, so was I. I came up with this, which seems to work.

    # <-- Script --------->

    $o = New-Object -comobject outlook.application
    $n = $o.GetNamespace("MAPI")

    $f = $n.PickFolder()

    $filepath = "c:\temp\"
    $f.Items| foreach {
     $SendName = $_.SenderName
       $_.attachments|foreach {
        Write-Host $_.filename
        $a = $_.filename
        If ($a.Contains("xlsx")) {
        $_.saveasfile((Join-Path $filepath "$SendName.xlsx"))
       }
      }
    }

    # <------ End Script ---------------------------------->

    This script seems to break many of the rules I was taught. The forEach loop looks very strange.

    • Marked as answer by OldDog1 Tuesday, August 10, 2010 7:36 PM
    Tuesday, August 10, 2010 5:04 PM
  • because you didn�??t filter out emails without attachments so the first email it comes to in that box doesn�??t have one, and the value is null, and fails.
     
    in that script, where $a = $f.items, that�??s just assigning all emails because $f is the folder and .items is all items in that folder.
     
    so that item may not have an attachment, hence the null error
     
    I think you can do
     
     

    foreach ($item in $f.items)

    {

    $sender = $item.SenderName

    foreach($attach in $item.Attachments)

    {

    if($attach.filename.contains("xlsx"))

    {

    write-host "$sender sent $($attach.filename)"

     

    }

    }

    }

     
     
    I did this on my inbox and for the most part it worked. I would some times get the error you got and I cant figure out why. In testing I found that it identified that it had attachments when it didn�??t.
     
     
     
    • Marked as answer by OldDog1 Tuesday, August 10, 2010 7:36 PM
    Tuesday, August 10, 2010 4:23 PM

All replies

  • I'm not understanding what you want.  I think your asking how to save all e-mail attachments in the folder you have selected with pickfolder to your file system.  If so, this will do that:

    $filepath = "c:\"
    $f.Items| foreach {
      $_.attachments|foreach {
        $_.saveasfile((Join-Path $filepath $_.filename))
      }
    }
    Regarding the script you say is mangled you'll need to send us the link to what you are talking about.
    write-host ((0..56)|%{if (($_+1)%3 -eq 0){[char][int]("116111101110117102102064103109097105108046099111109"[($_-2)..$_] -join "")}}) -separator ""
    Monday, August 9, 2010 6:20 PM
  • Well, That does not find any attachments and I know that there at least 8 of them in the folder I am selecting.

    # <-- Script --------->

    $o = New-Object -comobject outlook.application
    $n = $o.GetNamespace("MAPI")

    $f = $n.PickFolder()

    $filepath = "c:\temp\"
    $f.Items| foreach {
      $_.attachments|foreach {
        Write-Host $_
        $_.saveasfile((Join-Path $filepath $_.filename))
      }
    }

    # <------ End Script ---------------------------------->

    As for the mangled Web page it's:

     http://blogs.technet.com/b/heyscriptingguy/archive/2008/06/23/how-can-i-open-and-close-outlook-attachments.aspx

    I know it's in vbScript, but I was hopping to geet some clues.

     

    Monday, August 9, 2010 8:57 PM
  • Regarding the web page - It's because they're using <pre> tags on a page that has a formatted width.  Copy and paste the code sections or the entire page into something else and you should see everything. 

    Regarding the code you posted - I modified the filepath variable and ran your code.  I selected a folder with a few mailitems with attachments.  It saved 4 attachments from 2 e-mails out of 4 total e-mails in the folder with no problem.  Are you getting any errors?  Are you positive the folder you are selecting with pickfolder() is the folder?  The code won't pick up subfolders as it exists now; it will only get the items in the folder you selected.


    write-host ((0..56)|%{if (($_+1)%3 -eq 0){[char][int]("116111101110117102102064103109097105108046099111109"[($_-2)..$_] -join "")}}) -separator ""
    Monday, August 9, 2010 9:31 PM
  • You are right, it worked when I tried it again. Not at all sure why it did not the first few times I tried it. Monday, I guess.

    On another note.

    Now I want to save just the Excel attachments in the folder I pick.

    If I do this:

    $o = New-Object -comobject outlook.application
    $n = $o.GetNamespace("MAPI")

    $f = $n.PickFolder()

    $filepath = "c:\temp\"
    $a = $f.Items
    $b = $a.Count

    foreach ($item in $a) {
     Write-Host $item
     $atAttachs = $item.Attachments

    }

    And then this:

    PS Scripts:\> $atAttachs | gm


       TypeName: System.__ComObject#{00063007-0000-0000-c000-000000000046}

    Name             MemberType Definition
    ----             ---------- ----------
    Delete           Method     void Delete ()
    SaveAsFile       Method     void SaveAsFile (string)
    Application      Property   _Application Application () {get}
    BlockLevel       Property   OlAttachmentBlockLevel BlockLevel () {get}
    Class            Property   OlObjectClass Class () {get}
    DisplayName      Property   string DisplayName () {get} {set}
    FileName         Property   string FileName () {get}
    Index            Property   int Index () {get}
    MAPIOBJECT       Property   IUnknown MAPIOBJECT () {get}
    Parent           Property   IDispatch Parent () {get}
    PathName         Property   string PathName () {get}
    Position         Property   int Position () {get} {set}
    PropertyAccessor Property   PropertyAccessor PropertyAccessor () {get}
    Session          Property   _NameSpace Session () {get}
    Size             Property   int Size () {get}
    Type             Property   OlAttachmentType Type () {get}

     

    I can see that FileName is a Property I can {get} But if try this:

    $o = New-Object -comobject outlook.application
    $n = $o.GetNamespace("MAPI")

    $f = $n.PickFolder()

    $filepath = "c:\temp\"
    $a = $f.Items
    $b = $a.Count

    foreach ($item in $a) {
     Write-Host $item
     $atAttachs = $item.Attachments
     $FN = $atAttachs.FileName()    <--- Tried with and without the ()
     If ($FN.contains("xlsx")) {        #<---- this is line 26
     $strName = $item.SenderName
     Write-Host $strName 
     }
    }

    I get this:

    You cannot call a method on a null-valued expression.
    ERROR: At line:26 char:18
    ERROR: + If ($FN.contains <<<< ("xlsx")) {
    ERROR: + CategoryInfo : InvalidOperation: (contains:String) [], RuntimeException
    ERROR: + FullyQualifiedErrorId : InvokeMethodOnNull

    So how do I {get} the FileName or the SendersName?

    Tuesday, August 10, 2010 3:35 PM
  • because you didn�??t filter out emails without attachments so the first email it comes to in that box doesn�??t have one, and the value is null, and fails.
     
    in that script, where $a = $f.items, that�??s just assigning all emails because $f is the folder and .items is all items in that folder.
     
    so that item may not have an attachment, hence the null error
     
    I think you can do
     
     

    foreach ($item in $f.items)

    {

    $sender = $item.SenderName

    foreach($attach in $item.Attachments)

    {

    if($attach.filename.contains("xlsx"))

    {

    write-host "$sender sent $($attach.filename)"

     

    }

    }

    }

     
     
    I did this on my inbox and for the most part it worked. I would some times get the error you got and I cant figure out why. In testing I found that it identified that it had attachments when it didn�??t.
     
     
     
    • Marked as answer by OldDog1 Tuesday, August 10, 2010 7:36 PM
    Tuesday, August 10, 2010 4:23 PM
  • While you were working on this, so was I. I came up with this, which seems to work.

    # <-- Script --------->

    $o = New-Object -comobject outlook.application
    $n = $o.GetNamespace("MAPI")

    $f = $n.PickFolder()

    $filepath = "c:\temp\"
    $f.Items| foreach {
     $SendName = $_.SenderName
       $_.attachments|foreach {
        Write-Host $_.filename
        $a = $_.filename
        If ($a.Contains("xlsx")) {
        $_.saveasfile((Join-Path $filepath "$SendName.xlsx"))
       }
      }
    }

    # <------ End Script ---------------------------------->

    This script seems to break many of the rules I was taught. The forEach loop looks very strange.

    • Marked as answer by OldDog1 Tuesday, August 10, 2010 7:36 PM
    Tuesday, August 10, 2010 5:04 PM
  • yeah the $_ can get a little confusing with multiple foreach loops, which is why I structured mine more like your regular programming language. but basically we came up with the same thing.
     
     
    foreach($item in $object) { $item.stuff....... blah blah}
     
    vs
    $object | foreach {$_.stuff ........ blah blah}
     
    same thing...
    Tuesday, August 10, 2010 7:27 PM
  • Thanks for all the help.

    I was able to get my Excel attachment into my temp directory, open them and copy the data I needed to my Master.xlsx file.

    Now, if those folks would get busy and return my Surveys, I'll be golden.

     

    OldDog

    Tuesday, August 10, 2010 7:35 PM
  • Here is the example that you can refer

    http://msexchange.me/2013/09/20/outlook-automation-using-powershell/

    http://gallery.technet.microsoft.com/scriptcenter/Outlook-Automation-using-d7584688


    Sukhija Vikas

    Sunday, March 9, 2014 4:49 PM
  • "The forEach loop looks very strange."

    It looks strange because it is not a foreach command but the Foreach-Object pipeline command-let.

    It allows you to cycles through each object sent through the Pipeline (|) one by one.

    Very elegant (also Write-Output is a good replacement for Write-Host) and works with PowerShell V5 (64-bits) and even works with PowerShell V2 (64-bits)

    This script requires you create the C:\temp\ folder ahead of time.

    Also set the execution policies to something like:

    PS>Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

    Yes? Yes

    And to save that Script as a .ps1 file, I name the one I tested Get-OutlookAttachment.ps1

    Then 

    PS> ./Get-OutlookAttachment.ps1 # Script will prompt to chose an Outlook folder (assumes Outlook is installed and configured). I chose "Inbox"

    The resulting .xlsx are in C:\temp\ folder of course

    Notes: You could even replace "If ($a.Contains("xlsx"))" with something like "If ($a -match ".*xlsx")" for regex matching of multiple different extension types like : If ($a -match ".*xlsx|.*xls|.*txt")

    Thanks,

      Thierry

    Wednesday, July 6, 2016 12:14 PM