Thursday, December 14, 2017

Doing recursive CSV contact exports from Mailboxes and Public Folders

One of the scripts that I posted that seems to have a steady stream of input is the EWSContacts Module I put up some time ago. Thanks to Friedrich Weinmann who did some recent cleanup of the module on GitHub it is now looking a lot neater.

After a recent question from somebody I've made some more changes to the Export-EXCContactFolder function to allow you to if your exporting a Public Folder or a mailbox's Contacts Folder to recurse any subfolders of that folder you passed in and export those contacts also. When this option is selected a field is added to the CSV to tell you which MailboxFolder (or PublicFolder) the contact was exported from.

To use this new feature when exporting contacts from a Public folder just use the recurse switch eg

Export-EXCContactFolder -PublicFolderPath "\Office Contacts" -Recurse -MailboxName gscales@datarumble.com -FileName c:\temp\alc2.csv

Another parameter I've added is the -RecurseMailbox which will instead of starting at the default Contacts Folder in a Mailbox it will now start at the Mailbox root, iterate ever folder in the Mailbox and do a CSV export of any contacts in any folders in the Mailbox as long as those folder aren't hidden (a hidden folder generally indicates this is an Outlook System Folder so not something you want to process). Eg

Export-EXCContactFolder -RecurseMailbox -MailboxName gscales@datarumble.com -FileName c:\temp\alc2.csv
One last fix was to switch to using UTF8 encoding in the CSV so if your exporting contacts with non Ascii character it will now support this instead of showing a bunch of ???

The updated code is available on GitHub and I've also now published the Module here in the PowerShell Gallery To install the module from the gallery just use

Install-Module ExchangeContacts

Wednesday, November 29, 2017

Using OWA's Pin email feature using EWS in Office365 and Exchange 2016

Pinning email is a new feature that is available in OWA(or Outlook on the Web) on Office365 and Exchange 2016 and a much requested feature in Outlook https://outlook.uservoice.com/forums/322590-outlook-2016-for-windows/suggestions/12963459-pin-or-stick-important-emails-at-the-top-of-your-I

What happens when you pin an Email in OWA is it sets two MAPI properties on the backend when pinned the value will look like the following


the value 1/9/4500 is significant and is used when an Item in Pinned in the UI, When the Item is Unpinned in the UI 0x0F01 reverts back the current time and 0x0F02 is removed from the item. So in EWS if you want to display all the Pinned email you can use a SearchFilter to filter the Item based on the value of this property eg


 $PR_RenewTime2 = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0xF02,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime); 
 $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan($PR_RenewTime2, [DateTime]::Parse("4500-9-1"))
I've create a PowerShell EWS Module to enumerate the Pin Item in a folder here https://github.com/gscales/Powershell-Scripts/blob/master/PinEmail.ps1

To show Pinned Email in the Inbox you can use

Get-PinnedEmail -MailboxName gscales@datarumble.com -FolderPath \Inbox

To Unpin Email you can use the Set-UnPinEmail function to pass in a EWS Managed API Item type eg

 Set-UnPinEmail -Item $item

One real life example where this came in handy was I recently managed to pin over 800 emails in my mailbox through inadvertently enabling a InboxRule using the Pin Email action that procesed every incoming message. When I switched the rule off recently which i didn't think much off a the time I could then nolonger see any new messages arriving in OWA because they where appearing under the pinned email (800 deep in my mailbox no matter how I sorted the email). It didn't register what the problem was a first (as a long term user i just ignored the pin icon)and thought it maybe an OWA bug that would be fixed in the next service update and it embarrassingly took me a few weeks to realise what the actual problem was. Unpinning email one at a time would have taken me a very long time hence this script.

So to unpin all email in the Inbox you can use

Get-PinnedEmail -MailboxName gscales@datarumble.com -FolderPath \Inbox | foreach-object{Set-UnPinEmail -Item $_}

To fill out the function in this module there is also a Set-PinEmail function and an example function that uses this which will Pin the last received email in the Inbox eg

 Invoke-PinLastEmail -MailboxName gscales@datarumble.com -FolderPath \Inbox

All the code for this post can be found on GitHub here https://github.com/gscales/Powershell-Scripts/blob/master/PinEmail.ps1

Thursday, November 02, 2017

Simple Mail Gui's for the REST API on Office365 and Exchange 2016

As I've been adding new cmdlets to my Exch-REST PowerShell module I've been knocking up against some of the limitations and frustrations of working within a console environment. One of them is if say I find some Messages within a Mailbox which needs further investigation for whatever reason, It good to have an easy way of opening up that message to take a look at the content that isn't normally easy to view in the cmdline like the html body of a message and maybe I want to download any attachments or MessageHeaders without needing to open Outlook or OWA. A number of years ago I created a simple EWS mail client that just acted as a simple Email client using EWS so I decided to port this to the REST API and include it in the Exch-Rest Module. I also broke out two of the winforms from this simple client to enable just reading a particular Message you may have found using any of the other cmldets as well as a simple Form to Send a New Message with or without an Attachment.

So with Version 2.7 of the Exch-REST Module you can now use the

Start-EXRMailClient cmdlet which will start the small mail client which will use the underlying Exch-REST Module cmldets to open and browse Mailbox Mail folders, Read Items from a Folder you select in the TreeView, Show Internet Message Headers or Download attachments from messages in the datagrid eg


Getting Messages from the Inbox


Showing the content of a Message


or show the Message Headers



You can either start the Mailbox client using Start-EXRMailClient cmdlet with no parameters or pass in a Mailbox and AccessToken you maybe working with and it will automatically enumerate the Folders from that Mailbox using the token passed in.

To use the Read Message Form on a message you have found in the cmdline through using another cmdlet eg to open the last message in the Inbox you could use


$Items = Get-WellKnownFolderItems -MailboxName gscales@datarumble.com -AccessToken $AccessToken -WellKnownFolder Inbox -TopOnly:$true -Top 1
Invoke-EXRReadEmail -ItemRESTURI $Items[0].ItemRESTURI -AccessToken $AccessToken -MailboxName gscales@datarumble.com
To Start the New Message form just use

Invoke-EXRNewMessagesForm -MailboxName gscales@datarumble.com -AccessToken $AccessToken




Tuesday, October 17, 2017

Export Contacts from a Mailbox Contacts Folder or Public Folder to a CSV file using EWS and REST

As I had a few questions about exporting contacts to CSV from both Mailbox and Public folders I've added some new functions to the my EWS contacts module to make this easier.  I've added to new cmdlets the first is

Export-ContactsFolderToCSV

This will export a contacts folder to CSV's either the default Contacts Folder in a Mailbox or a secondary Contacts folder if you enter in the FolderPath eg to export the Default contacts Folder use

Export-ContactsFolderToCSV - MailboxName user@domain.com -FileName c:\exports\contacts.csv

To export a Secondary Contacts Folder use

Export-ContactsFolderToCSV - MailboxName user@domain.com -FileName c:\exports\contacts.csv -Folder \Contacts\SecondContactsFolder

Export-PublicFolderContactsFolderToCSV

This will export a Public Contacts Folder to a CSV file to use this use

Export-PublicFolderContactsFolderToCSV -MailboxName mailbox@domain.com -FileName c:\exports\PublicFolderexport.csv -PublicFolderPath \folder\folder2\contacts

The EWS Contacts Module is available on GitHub here https://github.com/gscales/Powershell-Scripts/blob/master/EWSContacts/EWSContactFunctions.ps1

I've also added some code to my Exch-REST module to allow export of a Contacts Folder using the new REST API on Office365 and Exchange 2016. To use this you need version 2.4 of the module from the PowershellGallery https://www.powershellgallery.com/packages/Exch-Rest then use

Export-ContactFolderToCSV -MailboxName tmpcontact@domain.com -AccessToken $AccessToken -FileName c:\exports\filename.csv




Wednesday, October 04, 2017

Showing all your Room Mailboxes in Office365 using the Graph REST API

One of the recurring questions you see asked in Forums around EWS is the ability to see all the Room Mailboxes in Exchange. This is pretty reasonable request that hasn't really had an easy answer in the past there are RoomLists but these required setup and ongoing maintenance and coordination  which can be troublesome. If you where using OnPrem exchange then instead of using EWS you could just use LDAP to lookup Active Directory directly (as well all know AD is the directory service for Exchange) eg back in 2007 I created this http://gsexdev.blogspot.com.au/2007/04/webservice-to-find-room-and-equipment.html.

With Office365 you don't have LDAP access to the directory and the Graph API while its good doesn't offer access to the particular property on a User or Directory Object that would allow you to tell if that object has an associated Room (or group or shared mailbox). The good news however is the Graph API  now gives you the ability to pull all the Room Mailbox using findrooms operation https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/user_findrooms

I've added support for this to my Exch-Rest Module in the Powershell gallery https://www.powershellgallery.com/packages/Exch-Rest  so you can now get this with a script like the following using the Beta namespace

$Token = Get-AccessToken -MailboxName mailbox@domain.com -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl urn:ietf:wg:oauth:2.0:oob -ResourceURL graph.microsoft.com -beta  
Find-Rooms -Mailbox mailbox@domain.com -AccessToken $Token 
and it returns a simple list of your Room mailboxes like


You could also pipe the results into another script to find the usages of those room mailboxes for the next day. To do this I use the Get-CalendarView cmdlet which makes a request for the next 24 hours of appointments using a CalendarView as documented in https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_list_calendarview (for this to work you need to have rights on all the Rooms you want to report on else you will get a 'Service Unavailable' error)

$MailboxName = "gscales@datarumble.com"
$Token = Get-AccessToken -MailboxName $MailboxName  -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl urn:ietf:wg:oauth:2.0:oob -ResourceURL graph.microsoft.com -beta  
$rptCollection = @()
Find-Rooms -Mailbox $MailboxName  -AccessToken $Token | foreach-object{
        $RoomAddress =  $_.address
        Get-CalendarView -MailboxName $_.address -StartTime (Get-Date) -EndTime (Get-Date).AddDays(1) -AccessToken $Token  | foreach-object{
                $rptObj = "" | Select Room,Organizer,Subject,Start,End
                $rptObj.Room = $RoomAddress 
$rptObj.Organizer = $_.organizer.emailAddress.name
                $rptObj.Subject = $_.subject
                $rptObj.Start = [DateTime]::Parse($_.start.datetime).ToString("yyyy-MM-ddTHH:mm:ss")
                $rptObj.End =  [DateTime]::Parse($_.end.datetime).ToString("yyyy-MM-ddTHH:mm:ss")
                $rptCollection += $rptObj
        }
}
$rptCollection
You can then turn the collection into a CSV or HTML file as you like



This full sample with HTML output and the module source code can be found on GitHub here https://github.com/gscales/Exch-Rest (sample is in samples)

Tuesday, September 12, 2017

Using the Office365/Exchange 2016 REST API Calendaring

When it comes to Messaging API's there no more complex thing then calendaring, not only because most vendors implement things a little differently but Exchange also has a little legacy feature drag because of its length of time in the market place and the difficulties in changing the technology and human interaction with it. The new REST API offers yet another window into calendaring on Office 365 and Exchange 2016 with a major security benefit over its predecessor which is the ability to be able to scope your application access so it can only access the Calendar folders in a Mailbox where previously apps may have needed to have Full Rights or impersonation rights on a Calendar. Also the new REST API allows you to interact with Group Calendars (or Teams Calendar) so leveraging the new REST API to value add to the adoption of these new features can be a worth your while.

To Support Calendaring I've added 2 new cmdlets to the Exch-Rest module to handle creating Appointments and Meeting and a few other supporting cmdlets to create the data structures necessary to handle each of the data types. I have a full documentation page with examples on GitHub here https://github.com/gscales/Exch-Rest/blob/master/Samples/CalendaringExamples.md

Calendaring Types

As I mentioned in my opening there is no more complex a thing then calendaring so lets look briefly at the converge I've added so far in the Module

Creating a Single Appointment in the default Calendar

Fairly basic thing to do you use the New-CalendarEventRest cmdlet to specify all the properties you want to set in this New Appointment

New-CalendarEventREST -MailboxName mailbox@datarumble.com -AcessToken $AccessToken -Start ([DateTime]::Parse("2017-07-05 13:00")) -End ([DateTime]::Parse("2017-07-05 14:00")) -Subject "Name of Event"

Creating a Single Appointment in the secondary Calendar

The new REST API allows you to enumerate all the calendars in a Mailbox and returns them as a flat list structure (rather then a parent/child like Mailboxes). To cater for creating appointments on Secondary Calendar's (eg any calendar other then the default) I included a -CalendarName parameter which first finds the secondary calendar using a filter based on the DisplayName and then allows you to create the appointment like in the first example eg


New-CalendarEventREST -MailboxName mailbox@datarumble.com -AcessToken $AccessToken -Start ([DateTime]::Parse("2017-07-05 13:00")) -End ([DateTime]::Parse("2017-07-05 14:00")) -Subject "Name of Event" -CalendarName 'Secondary calendar'
Creating a Single Appointment on a Group Calendar

To create an Event on a Group (or Teams) Calendar the user your using to access the REST API must be a member of that group in question and have the necessary rights on that Calendar. To cater for the necessary changes to the REST Uri you need to post to for groups which involves first finding the GroupId of the Group you want to access, I've added a -Group switch and -GroupName parameter to the cmdlets. The cmdlets then will use a lookup of the group based on its displayname to find the GroupId first and then POST to the Group calendar. (The organizer of the appointment you create on the Group Calendar becomes the Mailbox your posting as). Eg

#Creates a new all event for the 5th of July from 13:00-14:00 on the a group calendar called 'Powershell Module'
New-CalendarEventREST -MailboxName mailbox@datarumble.com -AcessToken $AccessToken -Start ([DateTime]::Parse("2017-07-05 13:00")) -End ([DateTime]::Parse("2017-07-05 14:00")) -Subject "Name of Event" -group -groupname 'Powershell Module'
Creating a Recurring Appointment

Appointment or Meeting Recurrence is probably the hardest thing when it comes to Calendaring for developers and admin to understand because there are so many different permutations of recurrence (eg daily,weekly,yearly etc). Discussing this in detail would consume many pages and hours so  I have covered the basics on https://github.com/gscales/Exch-Rest/blob/master/Samples/CalendaringExamples.md an example of creating a recurring appointment looks like

$days = @()
$days+"monday"
$Recurrence = Get-Recurrence -PatternType weekly -PatternFirstDayOfWeek monday -PatternDaysOfWeek $days -PatternIndex first  -RangeType enddate -RangeStartDate ([DateTime]::Parse("2017-07-05 13:00")) -RangeEndDate ([DateTime]::Parse("2019-07-05 13:00"))
#Creates a new all day event for Today on the default calendar
New-CalendarEventREST -MailboxName mailbox@datarumble.com -AcessToken $AccessToken -Start ([DateTime]::Parse("2017-07-05 13:00")) -End ([DateTime]::Parse("2017-07-05 14:00")) -Subject "Monday Meeting" -Recurrence $Recurrence
Meetings

A meeting from an API perspective is an appointment with Attendees, an Attendee can be Required, Optional or a Resource like a Meeting rooms. One of the things the REST API can't do which you can do in EWS is create Meetings with attendees and control whether Meeting invitations are sent or not (they will always be sent in REST). To help out with creation of the correct attendees structure I've included the New-Attendee cmdlet so it can be used like such to creating meetings with attendees

$Attendees = @()
$Attendees += (new-attendee -Name 'fred smith' -Address 'fred@datarumble.com' -type 'Required')
$Attendees += (new-attendee -Name 'barney jones' -Address 'barney@datarumble.com' -type 'Optional')

New-CalendarEventREST -MailboxName mailbox@datarumble.com -AcessToken $AccessToken -Start ([DateTime]::Parse("2017-07-05 13:00")) -End ([DateTime]::Parse("2017-07-05 14:00")) -Subject "Name of Event" -Attendees $Attendees
If there is anything missing or that you need to do but can't work out please post an issue https://github.com/gscales/Exch-Rest/issues on GitHub and I'll add a sample to demonstrate it.

Coming Soon

Coverage for Free/Busy

The module is available from the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest and the source from Github
https://github.com/gscales/Exch-Rest

Wednesday, August 23, 2017

Reporting on the Public Folder favorites in a Mailbox using EWS and Powershell

Outlook and OWA allow you to create "Favorites" shortcuts to Public folders (and other folders for that matter) to make accessing them a faster experience.


For those of us keeping pace with grinding Public Folders into dust (personally no longer a fan) and replacing them with Groups, it maybe useful to report on which Public Folders people have Favorited in their mailboxes for a metric and impact point of view. This information should then come in useful if your planing on migrating them (or just for a laugh). Because Public folder hierarchies are relatively complex having the path to the Public folder rather then just the name is generally a lot more useful. So in the script in this blog post we will look at producing a report of the Public Folder favorites and the Path of the Public Folders those shortcuts refer to eg it will produce a report something like the following for each mailbox you where to run it against.



Like other mailbox shortcuts these favorites are FAI (Folder associated items) stored in the Common Views Folder in the Non IPM Subtree folder of a Mailbox. Other names for these items are wunderbar or navigation shortcuts and are documented in https://msdn.microsoft.com/en-us/library/ee157359(v=exchg.80).aspx

EWS allows relatively easy access to FAI items in a Mailbox folder by using an Associated Item traversal in the FindItems operation. eg

$ivItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Associated
 To restrict the query to just returning those items that are public folder shortcuts you need to put a restriction on the  PidTagWlinkFlags property.

The actual properties on the FAI Items must be accessed using extended properties in EWS which are documented in the Exchange protocol document linked above. To work out the path to folder you need to get the PidTagWlinkEntryId property which returns the EntryId of the PublicFolder that the shortcut points to which you then need to convert to an EWSId to access the Public Folder in question. However it is not quite as straight forward as this because the EntryId that is returned by this property isn't convertible with EWS ConvertId operation because of one of the flag properties used. So some transposition of the EntryId is required, because the EntryId format is documented here https://msdn.microsoft.com/en-us/library/ee217297(v=exchg.80).aspx transposing out the incompatible flags so the EntryId can then be used in EWS's convertId operation isn't that hard. eg


$TransposedId = "000000001A447390AA6611CD9BC800AA002FC45A0300" + $Id.Substring(44)
(1A.44.73.90.AA.66.11.CD.9B.C8.00.AA.00.2F.C4.5A. is the provider Id for Public folder and 0300 is folderType for PublicFolder)

Once you have the EWSId of the Public Folder you can then access the PR_Folder_Path property of the folder which should return the full path (with Unicode separators) to the Public Folder in question which you can then add to a report. From a permissions perspective this script needs to be able to access the CommonViews folder in a Mailbox and the Public Folder that the shortcut refers to which maybe a complex thing in some circumstances. As an alternative to binding to the Public Folder using EWS to get the Public folder Path in question you could also use Remote Powershell (Get-PublicFolder) as a alternative if permissions cause an issue.

I've put a copy of this script on Git hub https://github.com/gscales/Powershell-Scripts/blob/master/GetPfShortCuts.ps1

To run this script use Get-PublicFolderShortCuts -MailboxName gscales@datarumble.com

Friday, August 11, 2017

Generic Folder Picker for Mailboxes for use in REST based Powershell Office365 / MSExchange Scripts

This is a continuance of my Office365/Exchange 2016 Rest series, in this post I'll be showing how some of the new GUI elements in the Exch-Rest module can help improve the speed and usability of any scripts where you need to browse Mailbox folders. What this modification to the Module does is allows you to enumerate all the Mail Folders in a Mailbox using the REST API and then present a simple folder tree that then allows you to browse through the folder in a Mailbox you might be looking for and when you double click that folder it will then return that as an object that can be piped into further operations. eg the new cmdlet works like

Invoke-MailFolderPicker -MailboxName gscales@datarumble.com -AccessToken $AccessToken

and visually this is what it looks like


I have one extra feature I've added into the module that allows you to return the same folder tree as above with the folder sizes in MB. To do this you need to make sure of the Extended Property (PR_MESSAGE_SIZE_EXTENDED Mapi property). To use this run the Invoke-MailFolderPicker cmdlet with the -ShowFolderSize switch eg

Invoke-MailFolderPicker -MailboxName gscales@datarumble.com -AccessToken $AccessToken -ShowFolderSize 

will return something like


How it works 

The Invoke-MailFolderPicker cmdlet is an amalgam of other functions in the Exch-Rest module (this is actually the main reason I've been building this module in the first place while the individual functions within the module are relatively easy to reproduce you need a large library of these small functions to build things that are really interesting and useful). 

So to build a Tree we first need a root so getting the Root Folder of the Mailbox is done using

$rootFolder = Get-RootMailFolder -AccessToken $AccessToken -MailboxName $MailboxName

The important part here is to know the Id of the root folder so when you look at the parent FolderId properties of any child folder you can construct a Tree of folders from that.

To cater for Foldersizes if the ShowFolderSize switch  is used you need to use an Extended property because there is no strongly typed property for FolderSize that is returned by the REST API (this was the same in EWS as well). To do this the following code snippet is used

    $PropList = @()
    $FolderSizeProp = Get-TaggedProperty -Id "0x0E08" -DataType Long
    $PropList += $FolderSizeProp
    $Folders = Get-AllMailFolders -MailboxName $MailboxName -AccessToken $AccessToken -PropList $PropList

This builds out the correct expansion string for these extended properties as well as catering for the differences between the graph and Outlook endpoints. The Get-AllMailFolder cmdlets just gets all the child folders of the Mailbox and returns them as a collection. Finally the 

 Invoke-FolderPicker -MailboxName $MailboxName -Folders $Folders -rootFolder $rootFolder -pickerType mail -ShowFolderSize

Using the Windows form and the Treeview control to build a visual representation of the Mailbox folder tree and return it to initiator.

Practical use

I've come up with a practical sample of this located https://github.com/gscales/Exch-Rest/blob/master/Samples/showLast10.ps1 that will once you double click the target folder will return the last 10 items in a folder to the PowerShell window. This is a simple 4 line script including the code to get the Token

$MailboxName = "gscales@datarumble.com"
$AccessToken = Get-AccessToken -MailboxName $MailboxName -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl urn:ietf:wg:oauth:2.0:oob -ResourceURL graph.microsoft.com
$FolderSelected = Invoke-MailFolderPicker -MailboxName $MailboxName -AccessToken $AccessToken -ShowFolderSize
Get-FolderItems -AccessToken $AccessToken -MailboxName $MailboxName -Folder $FolderSelected -Top 10 -TopOnly:$true -ReturnSize | Select Subject,
receivedDateTime,singleValueExtendedProperties
All the scripts in this blog require version 2.1 of the Exch-Rest module from the PowerShell gallery https://www.powershellgallery.com/packages/Exch-Rest/2.1

Wednesday, July 19, 2017

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.

Wednesday, July 05, 2017

Simple scripted download Attachment using the Graph or Outlook Rest API in Office365

This is a rewrite of one of the more popular EWS posts (original post) on my blog about creating a scripted process that would download attachments from an email with a particular subject line in the Inbox and then mark that email read and move it to another folder in the Mailbox. In this post I'm going to go through a direct one to one rewrite of this script and talk about the comparisons between how you used the operations in EWS and now how you can do the same thing using the Graph or Outlook REST API. In another post I'll show an enhanced version that allows you to use the Graph API to save the attachment into another Graph endpoint such as OneDrive or SharePoint.

For this script I'm using the Exch-Rest Module I'm currently building which is available on the powershellgallery https://www.powershellgallery.com/packages/Exch-Rest and the source from Github https://github.com/gscales/Exch-Rest. For this script I had to overcome an issue with the ConvertFrom-Json Cmdlet which has an issue once the JSON payload gets over 2 MB so an alternate method in the REST code was needed to allow for attachment downloads of over 2MB.

Step 1: Loading the Module, Setting up variables for the script and Getting the Access Token

Import-Module Exch-Rest -Force
$MailboxName = "user@domain.onmicrosoft.com"
$Subject = "Daily Export"
$ProcessedFolderPath = "\Inbox\Processed"
$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         

The first step in the scripted process is to load the module then setup some variables to hold which mailbox to access, the subject of the Message to search for and the folder to move the message to once its has been processed. Then finally we have the code to get the oauth AccessToken,with getting the Access Token there are few different options described here you need to choose the most appropriate one for you depending on how you are going to implement this in your environment. The EWS equivalent here would have been loading the Managed API, authenticating and doing an Autodiscover

Step 2: Search for the Items in the Inbox that are Unread, Have Attachments and the Subject line "Daily Export"
 
$Filter = "IsRead eq false AND HasAttachments eq true AND Subject eq '" + $Subject + "'"
$Items = Get-FolderItems -MailboxName $MailboxName -AccessToken $AccessToken -FolderPath \Inbox -Filter $Filter
In the REST API like EWS there are two different ways of searching for items
  • Filter
In EWS this was done using the SearchFilters Class (or a restriction in EWS proxy code) in REST this is done using the OData parameter $filter. The underlying mechanism the Exchange store uses for both EWS and REST to filter items and returned results is based on Restrictions (I still like the explanations in this article as to how they work https://technet.microsoft.com/en-us/library/cc535025(v=exchg.80).aspx) . Using Filters gives you the most flexibility when it comes to finding particular items based on particular properties but this does come at the cost of performance. You should try not to over complicate your filters too much as like any database your performance and application will suffer if the filters are written poorly with no regards to data structure or quantity.
  • Content Index Searches
In EWS a content index search could be done using AQS (Advance Query Syntax) and the QueryString Element in the FindItem operation. In REST you use the $Search OData parameter https://msdn.microsoft.com/en-us/office/office365/api/complex-types-for-mail-contacts-calendar#OdataQueryParams and the same AQS syntax you use in EWS. These searches should yield much better performance when you are searching folders with much higher Item counts because they utilize the Content indexes. There are limitations around using Search however because only certain properties are indexed and available for use (see https://technet.microsoft.com/en-us/library/jj983804(v=exchg.150).aspx) and there can be a delay in Indexes being updated.

I've used the $filter parameter in my above example which reflects the SearchFilter used in EWS in the original post but Search could also be used in this example.

Step 3: Processing the Attachments on the Items

Get-Attachments -MailboxName $MailboxName -ItemURI $item.ItemRESTURI -MetaData -AccessToken $AccessToken | ForEach-Object{
$attach = Invoke-DownloadAttachment -MailboxName $MailboxName -AttachmentURI $_.AttachmentRESTURI -AccessToken $AccessToken
$fiFile = new-object System.IO.FileStream(($downloadDirectory  + "\" + $attach.Name.ToString()), [System.IO.FileMode]::Create)
$attachBytes = [System.Convert]::FromBase64String($attach.ContentBytes)   
$fiFile.Write($attachBytes, 0, $attachBytes.Length)
$fiFile.Close()
write-host ("Downloaded Attachment : " + (($downloadDirectory + "\" + $attach.Name.ToString())))
 Like EWS when you enumerate the Items in folder using REST it won't return all the information about the Item for performance reasons. So like EWS where you would then use a GetItem request to get the Metadata information about attachments in REST you need to do a simular thing before you can proceed to downloading the attachment. In the module I have the Get-Attachments function for returning the MetaData about attachments (this writes a custom AttachmentRESTURI property into the results). Then the Invoke-DownloadAttachment function does the downloading of the attachments content. If the message has more then one attachment this is where you would modify the code to do additional processing (eg restriction on FileName, FileType etc).

Step4: Marking the Item as Read

$UpdateProps = @()
$UpdateProps += (Get-ItemProp -Name IsRead -Value true -NoQuotes)
Update-Message -MailboxName $MailboxName -AccessToken $AccessToken -ItemURI $item.ItemRESTURI -StandardPropList $UpdateProps
In EWS to mark an Item as read you would update the IsRead Strongly typed property and then call UpdateItem operation. In REST it is much the same procedure except we have some code that defines the Property you want to update in JSON (in this case the isRead property) and then it uses a HTTP Patch request to send the update to the server along with the URI of the Item you are updating.

Step5: Moving the Item to another folder

Move-Message -MailboxName $MailboxName -ItemURI $item.ItemRESTURI -TargetFolderPath $ProcessedFolderPath -AccessToken $AccessToken     
 In EWS to move an Item to another folder you use the MoveItem operation, in REST its fairly simular you invoke a move by sending the URI of the Item with /move appended to the end with a JSON payload of destination FolderId https://msdn.microsoft.com/en-us/office/office365/api/mail-rest-operations#move-or-copy-messages which is what Move-Message function does in the Module.

that's pretty much it I've posted the full code for this sample on GitHub here https://github.com/gscales/Exch-Rest/blob/master/Samples/simpleAttachDownload.ps1
 

Friday, June 09, 2017

Using the Office365/Exchange 2016 REST API to access Mailbox data using PowerShell part 5 Sending

Following on from my previous posts in this series, I've made a number of changes to the Exch-REST PowerShell  module to increase its functionality.

The first is that it now supports using either the Microsoft Graph endpoint or the Outlook REST endpoint which has been default so far. If you want to know which endpoint you should use have a look at https://dev.office.com/chooseapiendpoint . The advantage of utilizing the graph endpoint is that it allows you to hook into all the other Office365 services like Groups,OneDrive,SharePoint, Onenote etc using the same endpoint and token (I'll have some example in future posts).

To utilize the graph endpoint is easy all you need to do is add one additional parameter when you create the token to specify you want the AccessToken for the graph eg

$Token = Get-AccessToken -MailboxName mailbox@domain.com `
                         -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 `
                         -redirectUrl urn:ietf:wg:oauth:2.0:oob         
                         -ResourceURL graph.microsoft.com    
All the other cmdlets will then look at the AccessToken`s resource property using the new Get-Endpoint function and return the appropriate endpoint. If you want to use the preview endpoints you would need to tweak this function to return the appropriate preview endpoint.

function Get-EndPoint{
        param(
        [Parameter(Position=0, Mandatory=$true)] [psObject]$AccessToken,
        [Parameter(Position=1, Mandatory=$true)] [psObject]$Segment
    )
    Begin{
        $EndPoint = "https://outlook.office.com/api/v2.0"
        switch($AccessToken.resource){
            "https://outlook.office.com" {  $EndPoint = "https://outlook.office.com/api/v2.0/" + $Segment }     
            "https://graph.microsoft.com" {  $EndPoint = "https://graph.microsoft.com/v1.0/" + $Segment  }     
        }
        return , $EndPoint
        
    }
}
The only quirk I've come across with the difference between the two endpoints is when using extended properties the Name used to identify the Property is PropertyId in the REST endpoint and Id in Graph Endpoint (when making GET). The module has partial coverage for this but its an area that needs some work and will improve with future updates.

I've also added one more AccessToken creation function that allows you to pass in the username and password via a PSCredential. This requires that you have implicit authentication enabled in your app configuration manifest see oauth2AllowImplicitFlow

I've also added functions for Item Creation, Sending messages and Attachment enumeration and downloading and it now stores the Tokens in securestrings.

Also thanks to Elijah Gagne there is also some proper getting started documentation in the GitHub Repo  https://github.com/gscales/Exch-Rest/blob/master/README.md

I've added some samples for using the Send-MessageREST function which is what you would use when you want to send a Message. This function covers all the Send Message bases it can send a Message normally as a user, Send a Message On Behalf or SendAS, and also it can handle allowing you to set the ReplyTo header to allow a different reply to address as well as set Delivery and Read Recipients. The samples page can be found https://github.com/gscales/Exch-Rest/blob/master/Samples/SendMessageExamples.md .

A full example that gets an Access token to send a Message with an Attachment would look like

$Mailbox = "user@domain.com"
$Subject = "Test Message"
$Body = "Test Body of Message"
$To = @(New-EmailAddress -Address user@domain.com)

$Attachment = @("c:\temp\attachmentfile.doc")



$AccessToken = Get-AccessToken -MailboxName $Mailbox -ClientId 5471030d-f311-4c5d-91ef-74ca885463a7 -redirectUrl urn:ietf:wg:oauth:2.0:oob
Send-MessageREST -MailboxName $Mailbox  -AccessToken $AccessToken -ToRecipients $To -Subject $Subject -Body $Body -Attachments $Attachment   
The module is available from the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest and the source from Github
https://github.com/gscales/Exch-Rest


Monday, May 22, 2017

Mailbox Automapping forward and reverse map using Autodiscover and PowerShell

Auto-mapping was a feature that was introduced in Exchange 2010, where when you added Mailbox access permission via Add-MailboxPermission, Outlook would automatically add that Mailbox as an additional Mailbox. Outlook uses Autodiscover to get the information on these mailboxes that are auto mapped via the AlternateMailboxes user setting eg in XML it comes back like the following


One thing you can't see easily via the Exchange Managed Shell cmdlet is which ACE's (Access Control Entries) have Automapping enabled on a particular Mailboxes DACL and which don't. You also can't easily see from one mailbox which mailboxes will be auto mapped to it (basically a reverse mapping).So the purpose of this script is to provide both a forward and reverse mapping of Automapping setting for a collection of Mailboxes that are passed into to. It produces a report that look like




To get this collection you can either use a CSV file of addresses, Get-Mailbox or just a static list.

Eg to run the script using Get-Mailbox

create a blank collection
$Mailboxes = @()

populate the collection
Get-Mailbox -ResultSize Unlimited | foreach-object{$Mailboxes += $_.PrimarySMTPAddress}

then run the script like

 Get-AutoDiscoverMailboxMap -Mailboxes $Mailboxes

The report is outputted in the last line of the script.

I've put a copy of this script on GitHub here https://github.com/gscales/Powershell-Scripts/blob/master/AutoDiscoverMap.ps1

Tuesday, May 09, 2017

Using the Office365/Exchange 2016 REST API to access Mailbox data using PowerShell part 4

This is part 4 of my ongoing series for using the new REST API in Office365 and Exchange 2016. To make the module I created in previous posts a little more easier to use and open it up for other people to contribute to, I've published the module to the PowerShell Gallery and a new GitHub repository for contribution here https://github.com/gscales/Exch-Rest. So to install the latest version of the module from the PowerShell gallery on Windows 10 you just need to use

Install-Module Exch-Rest which will pull the latest version down from the Gallery

then Import-Module Exch-Rest to load the module

(For Windows 8.1 see http://go.microsoft.com/fwlink/?LinkID=746217&clcid=0x409 )

I've changed the authentication functions to allow you to pass in the ClientId, TenantId and RedirectURL to make it easier to use rather then having to use static variables. The documentation still needs a lot of work.

New functions I've added recently


Get-UserPhotoMetaData

     Gets the user's photo Metadata

Get-UserPhoto

     Download the largest UserPhoto

Folder Operations

New-Folder

    Creates a New Mail Folder (you can also create folder of a subclass of IPM.Note)

New-CalendarFolder

    Creates a New Calendar Folder

New-ContactsFolder

    Creates a New Contacts Folder

Rename-Folder

   Renames an existing folder

Update-Folder

    Updates an existing folder

Invoke-DeleteFolder

   Deletes and existing Mailbox Folder

Set-FolderRetentionTag

  Set a Personal retention Tag on a Folder this uses the Extended properties simular to this EWS script https://blogs.msdn.microsoft.com/akashb/2011/08/10/stamping-retention-policy-tag-using-ews-managed-api-1-1-from-powershellexchange-2010/

Extended property support

Tuesday, April 25, 2017

Using the Office365/Exchange 2016 REST API to access Mailbox data using PowerShell part 3

In Part 1 of this continuing series I looked at the basics of accessing a Mailbox using the new REST api in PowerShell with Exchange and in Part 2 accessing all the mailboxes in a tenant as an Administrative application using Certificate authentication. In this installment I've expanded the REST PowerShell module to encompass most of the common data operations like enumerating Folder and Items.

Getting a folder from Path

A common thing you might want to do with a script that accesses a Mailbox is access a non default folder eg say a folder that a user or application has created in the Inbox. To access a folder with the REST api you need to first know the Id of that particular folder. With the WellKnownFolders in a Mailbox you can use the constants such as Inbox,SentItems etc but for non default folders you need some code that will first find the Id of the Folder by searching for the folder in question in each of the parent folders. I've added a function in the Rest module called Get-FolderFromPath which will take the Path of the folder you want to access and break down and search for each of the folder in the Path and then return the target Folder eg


Enumerating the Folders in a Mailbox

If you want to enumerate all the Folders in a Mailbox in EWS you could do a deep traversal with the  FindFolders operation which would return all the folder paged in lot of 1000. With the REST API you need to traverse each of the ChildFolders separately to return all the folders. With Exchange in EWS there where 4 different strongly typed Folder objects Mail, Tasks, Contacts and Calendar, with EWS all of these where returned with the FindFolder operation. However with the REST API only Mail Folders are returned. With the Calendar, Contacts and Tasks folder you need to use the endpoints for those folder types which also have different permissions associated. This is done so you can have apps that can access all the calendar or contact folders in a Mailbox without the need to have permissions to traverse the Mail folders (and via verse).So if your migrating an existing script its going to requires a slightly different approach from what you may have traditionally done in other API's when doing this type of enumeration.

For enumerating all the Folders in a Mailbox I have created the Get-AllMailFolders function which also appends in the FolderPath as a value add eg


Enumerating Items in a Folder

Once you have retrieved a folder the next logical thing most people want to do is enumerate the Items in that Folder. To do this I've created a few different functions in the module the most common of those is the Get-FolderItems function. This function will either take the JSON Folder object or the path of the folder you want to access and then return each of the Items so you can use it like the following


this function handles paging of the folder Items in 1000 Item pages with involves using the @odata.nextLink return value and $Top and $Skip tokens from the response.

Enumerating All Items in a Mailbox

One of the things the REST API exposes easily that EWS didn't is the ability to enumerate all Items in a Mailbox using the AllItems WellKnowFolder name. I've created a function that exposes that endpoint for the AllItems folder eg

 Get-AllMailboxItems -MailboxName gscales@datarumble.com -AccessToken $tk

Enumerating Focussed Inbox Items

The replacement to Clutter in Office365 as a way of addressing Inbox overload in Exchange is the Focused Inbox which takes advantage of the rapid development of machine learning. From a programmatic point of view the focused Inbox is exposed as a Property InferenceClassification on a Message. So to show just focused Inbox items you can use the Filter token to query for those items that are just equal to focused eg

"Inbox/messages/?`$select=ReceivedDateTime,Sender,Subject,IsRead,InferenceClassification`&`$Top=1000`&`$filter=InferenceClassification eq 'Focused'"

I've created a function called Get-FocusedInboxItems for this which you can use like the following


Get the Mailbox GUID

One last thing that comes in handy sometimes when trying to correlate data is the mailbox GUID for different users. This is now more easily available in the REST api then it was in EWS using the users endpoint. I've put together a simple function to use this called Get-MailboxUser and it can be used like


The updated REST module can be found on GitHub here https://github.com/gscales/Powershell-Scripts/blob/master/RestHttpClientMod.ps1

Thursday, April 06, 2017

Using the Archive Item operation in EWS

As there has been a bit of talk about archiving lately and the new Archive Folder vs the Online Archive in Office365 https://support.office.com/en-us/article/Archive-in-Outlook-2016-for-Windows-25f75777-3cdc-4c77-9783-5929c7b47028?ui=en-US&rs=en-US&ad=US and https://www.petri.com/exchange-online-archive-options .I'd though I'd do a post about the Archive Item operation in EWS which was introduced in Exchange 2013 and is an interesting lever for admins to use if they want to look a manually archiving items to a Mailboxes online archive without using Archive Policies. The Archive Item operation is basically designed to allow you to move items from the Primary to the Online Archive more easily instead of having to batch a Move operation. To use this operation you need to enumerate the Items you want to archive (using a search filter or other restriction) and then provide that as one of the inputs for archive Items as longs as the folder where the Items are being archived from. The only real catch I've found when using it is that you need to use Impersonation or the Automated folder creation in the Archive doesn't seem to happen.

I've created a script that allows you to query a Mailbox folder using an AQS query based on the item received date and then invoke the Archive Items operation on those items and put this up on GitHub here https://github.com/gscales/Powershell-Scripts/blob/master/ArchiveItems.ps1

This script requires EWS impersonation https://msdn.microsoft.com/en-us/library/office/dn722376(v=exchg.150).aspx to run

To run the script you pass in the Mailbox you want to run the script against, the FolderPath where the Items exist that you want to archive and the Date of the Items you want to archive from. Eg to archive items older then one year you would run

Invoke-ArchiveItems -MailboxName user@domain.onmicrosoft.com -FolderPath "\Clutter" -queryTime (Get-Date).AddDays(-365)