Powershell script to export all attachments from a user mailbox and save them locally

%3CLINGO-SUB%20id%3D%22lingo-sub-1451852%22%20slang%3D%22en-US%22%3EPowershell%20script%20to%20export%20all%20attachments%20from%20a%20user%20mailbox%20and%20save%20them%20locally%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1451852%22%20slang%3D%22en-US%22%3E%3CP%3ESo%20it%20seems%20there%20are%20a%20bazillion%20ways%20to%20do%20this%20but%20I%20settled%20on%20the%20simplest%20one%20and%20I%20am%20getting%20an%20Invoke-RestMethod%20error.%26nbsp%3B%20Office%20365%20exchange%20online%20and%20I%20am%20testing%20with%20my%20user%20account%20and%20I%20am%20a%20global%20admin.%26nbsp%3B%20Any%20help%20is%20greatly%20appreciated%20-%20thanks.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20method%20I%20chose%20is%20listed%20here%20(The%20%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F637%22%20target%3D%22_blank%22%3E%40Brent%20Ellis%3C%2FA%3E%20reply)%3A%20%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fexchange%2Fneed-an-example-powershell-script-to-get-attachments-from-an%2Fm-p%2F4400%22%20target%3D%22_blank%22%3Ehttps%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fexchange%2Fneed-an-example-powershell-script-to-get-attachments-from-an%2Fm-p%2F4400%3C%2FA%3E%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EHere%20is%20my%20script%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3EConnect-EXOPSSession%20-UserPrincipalName%20xxx%40xxx..com%0A%0A%24Mailbox%20%3D%20%22myuser%40xxx.com%22%0A%24url%20%3D%20%22https%3A%2F%2Foutlook.office365.com%2Fapi%2Fv2.0%2Fusers%2F%24Mailbox%2Fmessages%22%0A%24date%20%3D%20%222020-06-01%22%0A%0A%23%23%20Get%20all%20messages%20that%20have%20attachments%20where%20received%20date%20is%20greater%20than%20%24date%20%0A%24messageQuery%20%3D%20%24url%20%2B%20%22%3F%60%24select%3DId%26amp%3B%60%24filter%3DHasAttachments%20eq%20true%20and%20DateTimeReceived%20ge%20%22%20%2B%20%24date%0A%24messages%20%3D%20Invoke-RestMethod%20%24messageQuery%20-Credential%20%24cred%0A%0A%23%23%20Loop%20through%20each%20results%0Aforeach%20(%24message%20in%20%24messages.value)%7B%0A%0A%20%20%20%20%23%20get%20attachments%20and%20save%20to%20file%20system%0A%20%20%20%20%24query%20%3D%20%24url%20%2B%20%22%2F%22%20%2B%20%24message.Id%20%2B%20%22%2Fattachments%22%0A%20%20%20%20%24attachments%20%3D%20Invoke-RestMethod%20%24query%20-Credential%20%24cred%0A%0A%20%20%20%20%23%20in%20case%20of%20multiple%20attachments%20in%20email%0A%20%20%20%20foreach%20(%24attachment%20in%20%24attachments.value)%7B%0A%20%20%20%20%20%20%20%20%24attachment.Name%0A%20%20%20%20%20%20%20%20%24path%20%3D%20%22c%3A%5CAttachments%5C%22%20%2B%20%24attachment.Name%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%24Content%20%3D%20%5BSystem.Convert%5D%3A%3AFromBase64String(%24attachment.ContentBytes)%0A%20%20%20%20%20%20%20%20Set-Content%20-Path%20%24path%20-Value%20%24Content%20-Encoding%20Byte%0A%20%20%20%20%7D%0A%0A%20%20%20%20%23%20Move%20processed%20email%20to%20a%20subfolder%0A%20%20%20%20%24query%20%3D%20%24url%20%2B%20%22%2F%22%20%2B%20%24message.Id%20%2B%20%22%2Fmove%22%0A%20%20%20%20%24body%3D%22%7B%22%22DestinationId%22%22%3A%22%22AAMkAGRiZmVmOTFlLWJmNjctNDVmZi1iZDkyLTZhOTEzZjI4MGJkNQAuAAAAAAAAkEHub27VS7X8pWwWnKIcAQCxICvUWGkmS6kBXjFB5cP%2FAADk%2Fq7pAAA%3D%22%22%7D%22%0A%20%20%20%20Invoke-RestMethod%20%24query%20-Body%20%24body%20-ContentType%20%22application%2Fjson%22%20-Method%20post%20-Credential%20%24cred%0A%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3BThe%20error%20I%20get%20is%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3EInvoke-RestMethod%20%3A%20The%20remote%20server%20returned%20an%20error%3A%20(401)%20unauthorized%0AAt%20C%3A%5Cnew-pw-dl-attach.ps1%3A9%20char%3A13%0A%2B%20%24messages%20%3D%20Invoke-RestMethod%20%24messageQuery%20-Credential%20%24cred%0A%2B%20categoryInfo%20%20%20%20%20%20%3A%20InvalidOperation%3A%20(System.Net.HttpWebRequest%3AHttpWebRequest)%20%5BInvoke-RestMethod%5D%2C%20WebException%0A%2B%20FullyQualifiedErrorId%20%3A%20WebCmdletWebResponseException%2CMicrosoft%2CPowershell.Commands.InvokeRestMethodCommand%0A%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-1451852%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EExchange%20Online%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EOffice%20365%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1458586%22%20slang%3D%22en-US%22%3ERe%3A%20Powershell%20script%20to%20export%20all%20attachments%20from%20a%20user%20mailbox%20and%20save%20them%20locally%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1458586%22%20slang%3D%22en-US%22%3EHello%20Derek%2C%3CBR%20%2F%3E%3CBR%20%2F%3EI%20would%20advice%20you%20to%20take%20a%20look%20at%20New-ComplianceSearch%20-ContentMatchQuery%20and%20include%20the%20KQL%20query%20%22hasattachments%3Atrue%22%3A%3CBR%20%2F%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fpowershell%2Fmodule%2Fexchange%2Fnew-compliancesearch%3Fview%3Dexchange-ps%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fpowershell%2Fmodule%2Fexchange%2Fnew-compliancesearch%3Fview%3Dexchange-ps%3C%2FA%3E%3CBR%20%2F%3E%3CBR%20%2F%3EAfter%20that%20you%20can%20export%20the%20results%20to%20a%20PST%20file.%3CBR%20%2F%3ETake%20a%20look%20at%20example%204%3A%3CBR%20%2F%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fpowershell%2Fmodule%2Fexchange%2Fnew-compliancesearchaction%3Fview%3Dexchange-ps%23examples%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fpowershell%2Fmodule%2Fexchange%2Fnew-compliancesearchaction%3Fview%3Dexchange-ps%23examples%3C%2FA%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1472214%22%20slang%3D%22en-US%22%3ERe%3A%20Powershell%20script%20to%20export%20all%20attachments%20from%20a%20user%20mailbox%20and%20save%20them%20locally%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1472214%22%20slang%3D%22en-US%22%3E%3CP%3ETry%20using%20this%20to%20pass%20the%20user%20credentials%2C%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%3CSPAN%20class%3D%22hljs-variable%22%3E%24UserCredential%3C%2FSPAN%3E%3CSPAN%3E%20%3D%20%3C%2FSPAN%3E%3CSPAN%20class%3D%22hljs-pscommand%22%3EGet-Credential%3C%2FSPAN%3E%3C%2FP%3E%3CP%3E%3CSPAN%20class%3D%22hljs-pscommand%22%3E%3CFONT%3E%24Session%20%3D%20New-PSSession%20-ConfigurationName%20Microsoft.Exchange%20-ConnectionUri%20%5B%23%24dp90%5D%3CA%20href%3D%22https%3A%2F%2Foutlook.office365.com%2Fpowershell-liveid%2F%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3Ehttps%3A%2F%2Foutlook.office365.com%2Fpowershell-liveid%2F%3C%2FA%3E%20-Credential%20%24UserCredential%20-Authentication%20Basic%20-AllowRedirection%3C%2FFONT%3E%3C%2FSPAN%3E%3C%2FP%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F106222%22%20target%3D%22_blank%22%3E%40Derek%20Gillespie%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E
Contributor

So it seems there are a bazillion ways to do this but I settled on the simplest one and I am getting an Invoke-RestMethod error.  Office 365 exchange online and I am testing with my user account and I am a global admin.  Any help is greatly appreciated - thanks.

 

The method I chose is listed here (The @Brent Ellis reply): https://techcommunity.microsoft.com/t5/exchange/need-an-example-powershell-script-to-get-attachments...

 

Here is my script:

Connect-EXOPSSession -UserPrincipalName xxx@xxx..com

$Mailbox = "myuser@xxx.com"
$url = "https://outlook.office365.com/api/v2.0/users/$Mailbox/messages"
$date = "2020-06-01"

## Get all messages that have attachments where received date is greater than $date 
$messageQuery = $url + "?`$select=Id&`$filter=HasAttachments eq true and DateTimeReceived ge " + $date
$messages = Invoke-RestMethod $messageQuery -Credential $cred

## Loop through each results
foreach ($message in $messages.value){

    # get attachments and save to file system
    $query = $url + "/" + $message.Id + "/attachments"
    $attachments = Invoke-RestMethod $query -Credential $cred

    # in case of multiple attachments in email
    foreach ($attachment in $attachments.value){
        $attachment.Name
        $path = "c:\Attachments\" + $attachment.Name
    
        $Content = [System.Convert]::FromBase64String($attachment.ContentBytes)
        Set-Content -Path $path -Value $Content -Encoding Byte
    }

    # Move processed email to a subfolder
    $query = $url + "/" + $message.Id + "/move"
    $body="{""DestinationId"":""AAMkAGRiZmVmOTFlLWJmNjctNDVmZi1iZDkyLTZhOTEzZjI4MGJkNQAuAAAAAAAAkEHub27VS7X8pWwWnKIcAQCxICvUWGkmS6kBXjFB5cP/AADk/q7pAAA=""}"
    Invoke-RestMethod $query -Body $body -ContentType "application/json" -Method post -Credential $cred

}

 The error I get is:

Invoke-RestMethod : The remote server returned an error: (401) unauthorized
At C:\new-pw-dl-attach.ps1:9 char:13
+ $messages = Invoke-RestMethod $messageQuery -Credential $cred
+ categoryInfo      : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft,Powershell.Commands.InvokeRestMethodCommand

 

 

2 Replies
Hello Derek,

I would advice you to take a look at New-ComplianceSearch -ContentMatchQuery and include the KQL query "hasattachments:true":
https://docs.microsoft.com/en-us/powershell/module/exchange/new-compliancesearch?view=exchange-ps

After that you can export the results to a PST file.
Take a look at example 4:
https://docs.microsoft.com/en-us/powershell/module/exchange/new-compliancesearchaction?view=exchange...

Try using this to pass the user credentials,

 

$UserCredential = Get-Credential

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection

@Derek Gillespie