Skip to main content

Using the Office365/Exchange 2016 REST API for working with Mailbox and OneDrive files/attachments

In my last post I demonstrated how to rewrite a simple download process script from using EWS to using the new REST API in Office365. This was an example of an Automation process that I first used in the early 2000's as back then email offered a ubiquity for a simple data transfer that even today is hard to do in some enterprises which is why that post has been so popular over the years.

In this post I'm going to show you how you can make use of other Office365 endpoints available in the Graph API to basically take this old process and start improving on it. The particular Endpoint I'll be focusing on in this post is OneDrive https://dev.onedrive.com/ (or One Drive for business). I'll also be using my Exch-Rest Module and taking advantage of all the existing code I've written for the interacting with  Exchange Online to interact with the OneDrive endpoint. And that really is the one great thing about the new Graph Endpoint is that all I've needed to do to add OneDrive functionally to my module is to create some new functions to deal with the specific addressing differences between Mail and One Drive while the underlying Auth, Get and Post function are the same (so speed and agility in marketing fluff and less hours recoding in real speak).

Basics of Interacting with One Drive

Authentication : To use One Drive from the Exch-Rest Module the application registration you have been using for your Mail scripts needs to be granted the OneDrive Permission Grants in the Azure portal https://dev.onedrive.com/auth/graph_oauth.htm. Once the grants have been added they will be then returned when AccessToken is requested, so basically you can use the same AccessToken for accessing Email and OneDrive without the need to authenticate again.

Paths and Drives:
 Each user has a default OneDrive drive which you can access using the /drive/root path . If you know the Id of the file you want to access then you can use the following Path drive/items/{item-id} to access it otherwise if you know the relative path (eg folder path/filename) then this can be used like /drive/root:/path/to/file to access the file. (this is a pretty basic explanation but the documentation cover this in more depth https://dev.onedrive.com/ )

 This will make more sense when you look at an example so lets start

Modern Attachments aka Reference Attachments

Modern Attachments is a new feature introduced into Exchange,Office365 and Outlook 2016 that was a kind of inbuilt OneDrive and Exchange integration feature. In EWS and REST these type of attachments are called reference attachments . Within the REST API currently Reference attachments are supported in the v2.0 Outlook endpoint and the Beta endpoint in the current Graph API. I've added support for sending reference attachments in Exch-Rest (v1.9) so an example of sending a reference attachment (eg a one drive file) using the Graph Beta Endpoint using the module would look like

Import-Module Exch-Rest -Force
$MailboxName = "gscales@datarumble.com"
$OneDriveFile = "/test/test2/fileName.zip"
$DownloadDirectory = "c:\temp"
##Get the Access Token
$AccessToken =  Get-AccessToken -MailboxName $MailboxName  -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl "urn:ietf:wg:oauth:2.0:oob" -ResourceURL graph.microsoft.com -Beta 
##Get OneDrive DownloadURI
$OneDriveAttachmentToSend = Get-OneDriveItemFromPath -MailboxName $MailboxName -AccessToken $AccessToken -OneDriveFilePath $OneDriveFile
$rtArray = @()
$rtArray += (New-referanceAttachment -Name $OneDriveAttachmentToSend.Name -SourceUrl $OneDriveAttachmentToSend.webUrl -Permission Edit)
Send-MessageREST -MailboxName $MailboxName  -AccessToken $AccessToken -ToRecipients @(New-EmailAddress -Address glenscales@yahoo.com) -Subject "Daily Send" -Body "See Attached" -ReferanceAttachments $rtArray
 Full Source Available here

In this example the Get-OneDriveItemFromPath function in the module is being used to get the necessary Reference Attachment properties (Name and WebURL) from the OneDrive file using the relative onedrive path. The New-referanceAttachment function creates a custom object that then Send-MessageREST function will be able to construct the necessary REST message to send.

Downloading a File from One Drive and Sending it as a normal Attachment

Modern Attachments are great but a lot of the time you will just need to send a particular one drive file as a regular attachment. In this case we need some more code to download the file you want to send from One Drive so it can then be Sent normally eg here is an example of doing this

Import-Module Exch-Rest -Force
$MailboxName = "gscales@datarumble.com"
$OneDriveFile = "/test/test2/fileName.zip"
$DownloadDirectory = "c:\temp"
##Get the Access Token
$AccessToken =  Get-AccessToken -MailboxName $MailboxName  -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl "urn:ietf:wg:oauth:2.0:oob" -ResourceURL graph.microsoft.com 
##Get OneDrive DownloadURI
$OneDriveAttachmentToSend = Get-OneDriveItemFromPath -MailboxName $MailboxName -AccessToken $AccessToken -OneDriveFilePath $OneDriveFile
$DownloadFileName = $DownloadDirectory + "\" + $OneDriveAttachmentToSend.Name
Invoke-WebRequest -Uri $OneDriveAttachmentToSend.'@microsoft.graph.downloadUrl' -OutFile $DownloadFileName
Send-MessageREST -MailboxName $MailboxName  -AccessToken $AccessToken -ToRecipients @(New-EmailAddress -Address user@domain.com) -Subject "Daily Send" -Body "See Attached" -Attachments @($DownloadFileName)
Full Source Available here

In this example the Get-OneDriveItemFromPath function is used again to get the details of the One Drive file using its friendly relative path. The property that is used this time is the @microsoft.graph.downloadUrl' which is a preautheticated short term download URL that is then used in Invoke-WebRequest to download the file from OneDrive to the local file system so it can just then be sent as a regular file attachment in Send-MessageREST.

Saving an Attachment from a Message to One Drive (Simple scripted download Attachment script modification)

In the last example I'm looking at modifying my previous script from this post so instead of saving the attachment to disk it instead saves them to One Drive. I've used the simple upload method so this would mean the attachment size is limited to 4 MB maximum (you would need to use another method if you have files that are larger then this that need uploading).

Import-Module Exch-Rest -Force

#Import-Module Exch-Rest -Force
$MailboxName = "gscales@datarumble.com"
$Subject = "Daily Export"
$ProcessedFolderPath = "\Inbox\Processed"
$OneDriveUploadFilePath = "/test" 

##Get the Access Token
$AccessToken =  Get-AccessToken -MailboxName $MailboxName  -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl "urn:ietf:wg:oauth:2.0:oob" -ResourceURL graph.microsoft.com  
##Search the Inbox
$Filter = "IsRead eq false AND HasAttachments eq true AND Subject eq '" + $Subject + "'"
$Items = Get-FolderItems -MailboxName $MailboxName -AccessToken $AccessToken -FolderPath \Inbox -Filter $Filter
if($Items -ne $null){
   if($Items -is [system.array]){
         Write-Host ($Items.Count.ToString() + " Items Found ")
   }
   else{
        Write-Host "Found 1 item"
   }
   foreach ($item in $Items) {
        Write-Host ("Processing Item received " + $Item.receivedDateTime)
        $item
        Get-Attachments -MailboxName $MailboxName -ItemURI $item.ItemRESTURI -MetaData -AccessToken $AccessToken | ForEach-Object{
            $attach = Invoke-DownloadAttachment -MailboxName $MailboxName -AttachmentURI $_.AttachmentRESTURI -AccessToken $AccessToken
            $attachBytes = [System.Convert]::FromBase64String($attach.ContentBytes)   
            $OneDriveFilePath = $OneDriveUploadFilePath + "/" + $attach.Name.ToString()
            Invoke-UploadOneDriveItemToPath -AccessToken $AccessToken -MailboxName $MailboxName -OneDriveUploadFilePath $OneDriveFilePath -FileBytes $attachBytes 
            write-host ("uploaded " + $OneDriveFilePath + " to one drive")
        }
        $UpdateProps = @()
        $UpdateProps += (Get-ItemProp -Name IsRead -Value true -NoQuotes)
        Update-Message -MailboxName $MailboxName -AccessToken $AccessToken -ItemURI $item.ItemRESTURI -StandardPropList $UpdateProps
        Move-Message -MailboxName $MailboxName -ItemURI $item.ItemRESTURI -TargetFolderPath $ProcessedFolderPath -AccessToken $AccessToken                
   }
  
}
else{
    Write-Host "No Item found"
}
Full Source Available here

The main change in this script is in the attachment processing code where the Invoke-UploadOneDriveItemToPath is used. This function does a simple upload of the file to OneDrive https://dev.onedrive.com/items/upload_put.htm so the total size of the file can't exceed 4MB.

All the scripts in this blog require version 1.9 of the Exch-Rest module from the PowerShell gallery https://www.powershellgallery.com/packages/Exch-Rest/1.9

The OneDrive API offers a lot more functionality that I'll share with more in future posts.

Popular posts from this blog

Downloading a shared file from Onedrive for business using Powershell

I thought I'd quickly share this script I came up with to download a file that was shared using One Drive for Business (which is SharePoint under the covers) with Powershell. The following script takes a OneDrive for business URL which would look like https://mydom-my.sharepoint.com/personal/gscales_domain_com/Documents/Email%20attachments/filename.txt This script is pretty simple it uses the SharePoint CSOM (Client side object Model) which it loads in the first line. It uses the URI object to separate the host and relative URL which the CSOM requires and also the SharePointOnlineCredentials object to handle the Office365 SharePoint online authentication. The following script is a function that take the OneDrive URL, Credentials for Office365 and path you want to download the file to and downloads the file. eg to run the script you would use something like ./spdownload.ps1 ' https://mydom-my.sharepoint.com/personal/gscales_domain_com/Documents/Email%20attachments/filena

A walk-though using the Graph API Mailbox reports in Powershell

Quite recently the Reporting side of the Graph API has moved in GA from beta, there are quite a number of reports that can be run across various Office365 surfaces but in this post I'm going to focus on the Mailbox related ones. Accessing Office365 Reports using Powershell is nothing new and has been available in the previous reporting endpoint  https://msdn.microsoft.com/en-us/library/office/jj984326.aspx however from the end of January many of these cmdlets are now being depreciated in favour of the Graph API  https://msdn.microsoft.com/en-us/library/office/dn387059.aspx . Prerequisites  In comparison to using the Remote PowerShell cmdlets where only the correct Office365 Admin permissions where needed, to use the new Graph API reports endpoint you need to use OAuth for authentication so this requires an Application Registration  https://developer.microsoft.com/en-us/graph/docs/concepts/auth_overview  that is then given the correct oAuth Grants to use the Reports EndPoin

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.