Quantcast
Channel: Glen's Exchange and Office 365 Dev Blog
Viewing all 241 articles
Browse latest View live

Using the Office365/Exchange 2016 REST API part 2 buiding an Admin Runner using AppOnly tokens

$
0
0
This is part 2 in my REST series in which we will look at AppOnly tokens. These are the Tokens you would look to use when you want to write an application or script that would access every mailbox in an Office365 Tenant.  For an Admin or DevOps person looking at what they might want to do with the new REST API this is useful when your looking to write something that will tweak a config setting on all Mailboxes to comply with a certain Organization policy (no matter how insane) or do some custom Item task that isn't supported in any of the Admin cmdlets.

To simplify AppOnly tokens as much as I can they are an Oauth Access token that are requested using Certificate Authentication. Then depending on what Application permission scopes have been allowed for the app in Azure eg


your script or app will be able to access that particular Mailbox data across all the Mailboxes within your tenant. In EWS if you understood how impersonation worked this is a kind of an equivalent but a lot better from a security perspective. Eg if your just writing a script that needs to keep contacts in sync in a Mailbox (which I've seen and done many times) then you can just assign the "Read and write contacts in all mailboxes" scope for your application and nothing else and that's all your application can do. With Impersonation you effectively gave full access to Mailbox and which could then be potentially exploited and was much derided by security people in general.

Setup
 
There is bit of setup to do before you can use this which can be little complicated but I'll try to simplify the best I can.

1. You need to first create a Web Application registration within your tenant using the Azure Management Console (note a native app won't work for this). https://docs.microsoft.com/en-us/azure/active-directory/active-directory-app-registration . For this type of application the SignIn and Auth url isn't important as we are going to be using certificate auth so you can just use something like https://192.168.100.100:8000 eg


2. Configure the Application permissions for what you want your application to do eg the below screen again. For my test app I'm going to be using the "Read All Mailbox Setting" scope to create a script that read the Oof Message and timezone setting from all mailboxes passed in. Delegated permission aren't valid for this type of App as they won't be scoped for the this type of Access token.


Make sure you click save down the bottom to ensure the changes you make are committed

3. Create a self signed certificate that will be used to sign the JWT (Java Web Token) requests. To make this easy in my Rest module I've create a cmdlet to do this using the default New-SelfSignedCertificate Powershell cmd. However this will only work on Windows 10 because it requires the -provider switch which isn't available on early O/S. Alternatives you can use are MakeCert which there is an example of in https://msdn.microsoft.com/en-us/office/office365/howto/building-service-apps-in-office-365 or you could use OpenSSL

If you want to use my cmdlet you need to use it like

Invoke-CreateSelfSignCert -CertName "yourCertNameMakeitdescriptive" -CertFileName c:\temp\yourcertFile.pfx -KeyFileName c:\temp\KeyCreds.txt

where CertName should be self explanatory, CertFileName is the filename for your certificate (you could chose to leave It in the windows CertStore but I've exported it to file for flexibility). -KeyFileName is a temporary text file that will hold the configuration information that needs to be copied in to the Application manifest in Azure in step 4.

4. Update the Application manifest, from the Azure Console where you configuration the application permissions in step2 you need to select the download manifest from the Manage Manifest option at the bottom of the console eg

Once you have download the manifest you need to open that in a Text editor (or VSCode if you have it) and locate the keycredentials entry


If you then open the KeyFileName that was created with Invoke-CreateSelfSignCert in step 3 and cut and paste the all the content and replace the [] in Keycredentials in the manifest you downloaded eg it should look something like this

 
Make sure when your pasting the data you don't wipe the comma after the value as you will get a parse error when you try to upload the manifest. So once you save those change you click upload Manifest from the Manage Manifest button in the Azure console.

5. The last thing you want to do while in the Azure console is get your tenant ID information there are a number of ways to do this but the easiest is outlined in https://support.office.com/en-us/article/Find-your-Office-365-tenant-ID-6891b561-a52d-4ade-9f39-b492285e2c9b  (just look at the URL when you modifying the application). You will need this TenantId for the Module Setup

PowerShell Module Setup

In my REST Script module I have a config section at the top that holds the information needed for generating the token. For App only tokens you need to setup the following variables

function Get-AppSettings(){
        param(
       
        ) 
  Begin
   {
            $configObj = "" |  select ResourceURL,ClientId,redirectUrl,ClientSecret,x5t,TenantId
            $configObj.ResourceURL = "outlook.office.com"
            $configObj.ClientId = "084adbb3-f70c-498f-97a3-464e1f444d9a"
            $configObj.TenantId = "1c3a18bf-da31-4f6c-a404-2c06c9cf5ae4"
            $configObj.ClientSecret = ""
            $configObj.x5t = "3Y5+FU8x2tZ8DudO479K71ILQF8="
            $configObj.ValidateForMinutes = 60
            return $configObj           
         }   
}

ResourceURL should be fine unless you want to access the Graph API

ClientId should be the ClientId of your Application in Manifest file you downloaded previously take the value from appId which should be the first line in the manifest

redirectURL isn't used for AppOnly token

TenantId should be set to the value you retrieved in step 5 above.

x5t should be set to the customKeyIdentifier that you pasted into the KeyCredentials in the manifest file.

Testing

Once you have all the configuration done to test if it is working you use the Get-AppOnlyToken cmdlet to create an app only token

$Token  = Get-AppOnlyToken -CertFileName c:\temp\certfile.pfx

If you then look at the contents of the $Token variable you should have either an error or a Token

Putting it to use

I've included one example report in the REST Module that will work with AppOnly tokens. This produces a report of Mailbox setting using the GetMailboxSetting op https://msdn.microsoft.com/office/office365/APi/mail-rest-operations#get-all-mailbox-settings

To run this you pass in a collection of Mailbox addresess you want to run the report against (eg you could produce this using Get-Mailbox) or use a CSV file or just using something like

$Mailboxes = @()
$Mailboxes += "one@domain.com"
$Mailboxes += "two@domain.com"

$report = Get-MailboxSettingsReport -Mailboxes $Mailboxes -CertFileName c:\temp\certfile.pfx
$report | Export-Csv -NoTypeInformation -Path c:\temp\mbreport.csv

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

Using the Archive Item operation in EWS

$
0
0
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)

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

$
0
0
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

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

$
0
0
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

Mailbox Automapping forward and reverse map using Autodiscover and PowerShell

$
0
0
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

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

$
0
0
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


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

$
0
0
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
 

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

$
0
0
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.

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

$
0
0
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

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

$
0
0
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

Using the Office365/Exchange 2016 REST API Calendaring

$
0
0
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

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

$
0
0
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)

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

$
0
0
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




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

$
0
0
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




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

$
0
0
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


Doing recursive CSV contact exports from Mailboxes and Public Folders

$
0
0
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

Major update to my Exch-Rest Powershell Module for accessing Office365 and Exchange 2016 Mailboxes via REST

$
0
0
For about a year I've being working on a PowerShell Module for accessing the new REST API for Office365 (the Graph API) and Exchange 2016. I've been experimenting and listening to the various feedback I've been getting and finally have an update to share that addresses a lot of the useability issues (okay it was just too developer orientated).

Application registrations

Still probably the biggest stumbling block is not having an application registration, I now have a menu that allows to you use various default registrations for testing and also to save a default registration that you create. This means once you have your own registration setup you can save the ClientId so it will be used as the default when you next use the module.

Aliasing

A failure in proper planning when I initially created the module meant I didn't use a specific alias for the cmdlets in the module which meant it can cause a lot of conflicts if you have other modules loaded. With the new version all cmldets have a -EXR moniker.

Token Caching

With previous versions of the module you basically would authenticate and create an access token and store that in a local variable which you then had to pass into each of the cmdlets using the -AccessToken parameter along with the MailboxName. This meant a lot of typing and really broke the useability flow without really providing any security benefits. With the new module while all these old behaviours are still valid there is now a Connect-EXRMailbox cmdlet which is the new entry point cmdlet which will store the token and the name of the mailbox associated with the Token in a script variable (encrypted of course) but accessible by the module. This means you can run most of the cmdlets without specifying a Mailbox or Access Token if you just want to return details about the current mailbox that has been authenticated. Also when it comes to using Directory or Reporting cmdlets it eliminates the need for MailboxName which used mandatory parameter on all cmdlets. 

New Getting Started Guide 

With this type of project documentation is the hardest thing as its the least interesting and more time consuming things to get correct. I've written a new getting Started Guide for people that haven't used the module before. The old readme guide is still valid so if your looking to use App Tokens this going to be useful

New Cmdlets

I have a lot of new cmldets most aren't documented in anyway but I hope to put these more into Context in the new year on the blog and on GitHub. Things like accessing the Reporting API's is interesting which I've covered briefly in the new Getting Started guide.

The new module Exch-Rest is available from the PowerShell Gallery here or the source code which will contains all the latest code is available on GitHub here.

 Feedback and Contributions

As always I welcome any feedback or contributors who want to improve the module

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

$
0
0
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 EndPoint. Only one particular grant is need for this eg



This is a big advantage over the previous Web Service along with the benefits of using Token based authentication and different App usage scenarios it limits access to just what is needed to read reports which help limit the security risks associated.

Accessing the Reporting Endpoint

To access and Run the reports you will need code that first Authenticates and generates an oAuth Token based on your App registration. My Exch-Rest module can be used for this (Connect-EXRMailbox will imitate the token Authentication) or there are other scripts people have posted that can also do this https://gallery.technet.microsoft.com/Get-Office365-usage-f955ade4 .

Inputs and Outputs

Compared to the  Remote PowerShell cmdlets the richness of Input parameters for the reports at GA is limited (even when compared to the beta which was more a little rich). For most of the Mailbox Reports the only input parameter is the Period best explained in the documentation
"Specifies the length of time over which the report is aggregated. The supported values for {period_value} are: D7, D30, D90, and D180. These values follow the format Dn where n represents the number of days over which the report is aggregated" ref https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/reportroot_getmailboxusagedetail
So if your trying to feature match your current scripts against the new Graph API you may need to look at restructuring or rethinking your process a little, at the end of the day the data is same but you may need to post process it more at the client side.

On the Output side a CSV is generated by the Graph API and returned to the user, currently because the endpoint doesn't yet support the Filter Query parameter if you want to perform any filtering you will need to do it post process once the CSV is downloaded. (I would expect that in future updates the other QueryStrings will be supported which will improve the flexibility for those working with very larger result sets in Larger tenants.

The Actual Reports 

Given the refresh time of the Graph API it mostly likely this list is already out of date by the time you read this. All the screenshots are from the reporting Cmdlets in lastest version of my Exch-Rest Module which you can get from the PowerShell gallery https://www.powershellgallery.com/packages/Exch-Rest  to use the cmdlets you need to first authenticate using Connect-ExrMailbox with an app registration that has been granted the "Read Usage Report" permission.


GetMailboxUsageDetails https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/reportroot_getmailboxusagedetail

This report returns mailbox usage information or in plane English how big your Mailbox is and what the Quotas are. eg

The Remote PowerShell cmdlet Get-MailboxUsageDetailReport returned the same information with a few extra fields but these fields are retrievable via other Graph endpoints

Eg MailboxPlan is returned via the users endpoint eg https://graph.microsoft.com/v1.0/users('user@mailbox.com')/assignedLicenses if needed etc. Other information that is missing like percentage used can be calculated from other values in the data provided.

(What's missing which was also absent in the Office 365 Reporting Web Service is the ability to report on the size of the Archive Mailbox. I hope this feature will appear in the future as its a critical data point to track for people using Archives).

 getMailboxUsageMailboxCounts https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/reportroot_getmailboxusagemailboxcounts

This report gives you daily tracking numbers of Active Mailbox use in your org eg




This report returns information about the Mailbox size status to the quotas that are configured, you want to use this in conjunction with GetMailboxUsageDetails which will allow you to pinpoint the actual mailboxs this summary reports are referring to.



This report returns the aggregated storage usage across the tenant (?? does it include archives ??)



This report is one of the more useful report as it gives you Message activity of a particular user eg the following is the last 30 days of my usage


This is also one of the reports that takes a parameter other then the Period eg you can enter a particular date value for the date you want to report activity on. eg tracking over a period of time



This reports give you the Activity counts of you users doing particular activities (my tenant is kind of a lonely one)


getEmailAppUsageUserDetail https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/reportroot_getemailappusageuserdetail

This gives you details of the Client application use



This gives you summary information on App usages 



This gives summary information on Client App version usage



Searching Mailbox folders using the REST API In Powershell on Office365 and Exchange 2016

$
0
0

Preface

Searching Mailboxes is something I've written a fair bit about in the past but in this article I'm going to cover how searching has been surfaced in the new REST API on Office365 and Exchange 2016.  The first place to go when considering searching in Office365 is the Security and Compliance center https://support.office.com/en-us/article/run-a-content-search-in-the-office-365-security-compliance-center-61852fd9-fe8a-4880-a339-cb19ed3bff4a it offers the most feature rich and realistic search experience that is difficult to replicate with client facing Mailbox API's (eg things like RBAC's delegated access rights, litigation holds etc). Given the amount of data that can be stored in Mailboxes now its important to have a realistic expectation of the time it will take and difficulty of searching 100,000's items in multiple folders or millions of items in an Archive is important (at the time of writing Archives aren't accessible using the REST API).

Searching Mailboxes

Within an Office365 Mailbox there are number of different methods for performing a Search using the REST API for particular items within a particular Mailbox folder or across all folders in a mailbox. Depending on the properties you want to search on and the amount of data you expect to return in your search you should select the most appropriate method to use.

Using filters (or Restrictions)

Restrictions are the standard way in which searches are normally carried out in Outlook and Exchange, restrictions are property based searches on one or more properties and may involve a number of operators such as equals, greatorthan, lessthan, contains (for substrings) or startswith. In the REST API restriction are implemented via the $filter Query Parameter. Because the raw content is queried this type of search can take longer to perform if the underlying folder has a larger number of items or the restriction being used is very complicated. A common issue that can occur using filters is the Query will timeout or will be throttled because of the overall load it produces on the server. Some more background on restrictions can be found https://technet.microsoft.com/en-us/library/bb267022(v=exchg.80).aspx

Using KQL (or Exchange Search)

Exchange Search is the full text indexing feature of Exchange which indexes contents automatically (asynchronously) as email arrives in a Mailbox.This type of Search utilizes the KQL (Keyword Query Logic) that is also used with in Share-point for query processing. This is the faster and more efficient method of searching a Mailbox because it utilizes Full Text Indexes rather then needing to query the raw content and should be the preferred starting point for search. However there are some limitations on the properties that can be searched using this method and also on the amount of results that will be returned by a query. In the REST API KQL queries are performed using the $search Query Parameter.
Property restrictions : As Exchange only indexes certain properties for performance reason only certain properties can be searched using KQL a full list of the indexed properties the can be used can be found https://support.office.com/en-us/article/keyword-queries-and-search-conditions-for-content-search-c4639c2e-7223-4302-8e0d-b6e10f1c3be3#emailproperties
Maximum Result Sets: Exchange limits the maximum result set of Exchange Search queries to 250 Items in REST and EWS. with OnPrem Servers this value is adjustable via the MaxHitsForFullTextIndexSearches property https://support.microsoft.com/en-us/help/3093866/the-number-of-search-results-can-t-be-more-than-250-when-you-search-em however in Exchange OnLine (Office365) you can't adjust that value and need to work under the 250 item ceiling.

Using SearchFolders

Search Folders are Special Mailbox folders that contain no items but return linked Items based on a predefined search criteria (usually the same as you would use in Filters). Because these are constantly updated asynchronously by the Exchange Server they offer better performance when retrieving items based on a search criteria then doing a normal search using a filter. Search Folders are a good choice if you have static queries that don't require any dynamic input or you need to have a search that spans multiple folders. You do need to be aware that overuse of Search Folders can cause poor performance and they are updated by a background process so new items may not be instantly available as apposed to querying the Mailbox folder directly. Some more background information on Search Folders can be found
Currently there is no way of creating a SearchFolder programmaticly using the REST API but this can be done using EWS.

Search Examples using the Search-EXRMessage cmdlet from the Exch-Rest Module

Several of the cmdlets in the Exch-REST Module https://www.powershellgallery.com/packages/Exch-Rest allows the entering of both the Filter and Search criteria however the cmdlet specifically designed for search is the Search-EXRMessage cmdlet
Using Raw KQL
The -KQL Parameter allows you enter raw KQL to be used as a Mailbox Search if you do use this approach you should take care that some characters need to be escaped correct. Eg when search for an exact phrase in KQL the phrase should be encolesd in double quotes. When doing this in REST these double quotes need to be escape. Eg so if you where search for email with the subject has the following keywords you could use -KQL "subject:'termone termtwo'" where if you want to search for a phrase you should use -KQL 'subject:"this is a phrase""
Specifying the folders to Search
By default the Search-EXRMessage cmdlet will search all the Mail folders in a Mailbox using the AllItems Search Folder if no folder is specified in the cmdline. To limit the Search to a particular folder you can specify the folder using the -FolderPath eg
Search-EXRMessage -FolderPath \Inbox -KQL 'subject:"Happy Days"'
or use the -Wellknown parameter to specify one of the default folder such as Inbox or SentItems eg
Search-EXRMessage -Wellknow SentItems -KQL 'subject:"Happy Days"'
Returning Attachments details within Search Results
Returning attachment details when search for objects requires extra requests be made to the server which will affect the performance of search greatly so this isn't done by default. However if its import for you to get this information because of the type of search that you are trying to do then you can use the -ReturnAttachments swich. Not when using the -AttachmentKQL switch this is submitted by default.
Finding a Message from a Particular Sender
Find a Message from a Particular Sender by Name or Email Address using a Filter
by Email
Search-EXRMessage -MailboxName user@domain -from 'James@domain.com'  | select Subject,SenderEmailAddress,SenderName,FolderPath
Find a messages from a Particular Sender by Name or Email Address using KQL
by Name
Search-EXRMessage -MailboxName user@domain -kql 'From:James'  | select Subject,SenderEmailAddress,SenderName,FolderPath
by Email Address
Search-EXRMessage -MailboxName user@domain -kql 'From:James@domain.com'  | select Subject,SenderEmailAddress,SenderName,FolderPath
Find a Message from a Particular Sender on a Particular Date
using a Filter
Search-EXRMessage -MailboxName user@domain -ReceivedtimeFrom ([DateTime]::Parse('2018-01-01')) -ReceivedtimeTo ([DateTime]::Parse('2018-01-02')) -from 'james@domain.com' | select Subject,SenderEmailAddress,SenderName,FolderPath
using KQL
Search-EXRMessage -MailboxName user@domain -ReceivedtimeFromKQL ([DateTime]::Parse('2018-01-01')) -ReceivedtimeToKQL ([DateTime]::Parse('2018-01-02')) -KQL 'from:james@domain.com' | select Subject,SenderEmailAddress,SenderName,FolderPath
Find a Message with a Particular Subject (Exact Match)
using a Filter
Search-EXRMessage -MailboxName user@domain -subject 'And the Subject is' | select Subject,SenderEmailAddress,SenderName,FolderPath
using KQL
Search-EXRMessage -MailboxName user@domain -SubjectKQL 'And the Subject is' | select Subject,SenderEmailAddress,SenderName,FolderPath
Find a Message where the Subject contains a phrase
using a Filter
Search-EXRMessage -MailboxName user@domain -SubjectContains 'And the Subject' | select Subject,SenderEmailAddress,SenderName,FolderPath
using KQL
Search-EXRMessage -MailboxName user@domain -KQL "Subject:'And the Subject'" | select Subject,SenderEmailAddress,SenderName,FolderPath
Find a Message where the Subject starts with a phrase using a Filter
Search-EXRMessage -MailboxName user@domain -SubjectStartsWith 'And the Subject' | select Subject,SenderEmailAddress,SenderName,FolderPath
Find a Message where the Body contains a phrase
using a Filter
Search-EXRMessage -MailboxName user@domain -BodyContains 'Summer Sales' | select Subject,SenderEmailAddress,SenderName,FolderPath
using KQL
Search-EXRMessage -MailboxName user@domain -BodyKQL "Sumber Sale" | select Subject,SenderEmailAddress,SenderName,FolderPath
Find Attachments
You can search for attachments using the full attachment name or a partial name like the extension if you want to find all email with word documents for example.
Using KQL
Search-EXRMessage -MailboxName user@domain -AttachmentKQL ".doc" -ReturnAttachments 

Using SearchFolders

To use a Search folder you first need to know the FolderId value of the SearchFolder you wish to use, SearchFolder should be available under the NON_IPM_Subtree Finder folder. To get the available search folders in a Mailbox you can use the Get-EXRSearchFolders Cmldet eg
Get-EXRSearchFolders -MailboxName user@domain.com
To Restrict the results to a particular name SearchFolder you can use
Get-EXRSearchFolders -MailboxName user@domain.com -FolderName 'Voice Mail'
To enumerate the linked Items (Search Criteria items) from the Voice Mail SearchFolder use
$VoiceMail = Get-EXRSearchFolders -MailboxName user@domain.com -FolderName 'Voice Mail'
Get-EXRFolderItems -MailboxName user@domain.com -Folder $voicemail

Looking into the Microsoft Team's TeamChat folder in an Office365 Mailbox using EWS

$
0
0
If you looked at the folder structure in an Office365 Mailbox (or older Exchange Mailbox) using a MAPI editor so you could see all the Non_IPM_Subtree and hidden folders around 5 years ago it was a relatively simple picture. Today with the rapid pace of the change in Office365 its a little bit of minefield on new folders and hidden data that is stored and used by various applications in your Mailbox. A good case in point is the Files Hidden folder which Tony explains about in https://www.petri.com/mysterious-files-folder . Microsoft Teams is the new kid on the block in terms of collaboration applications that integrate into Mailboxes and other Office365 workloads. As you chat using the Microsoft Teams client as part of it compliance/discoverability process associated with Microsoft Teams a cloud-based process creates conversation items in a Hidden folder in your Office365 Mailbox called TeamChat (this is a Subfolder of the Conversation history). Because its a hidden folder users won't be able to see the content but the data stored is searchable (at the time of writing this there is very little official documentation that I can find Tony's post https://www.petri.com/teams-compliance-story and a few other forum posts give the best guide to what this is and how it works).

So because this is a hidden folder using the new Graph API won't work (although you can actually get to the Items using the Graph API by searching) so using EWS is the best approach for now as it gives the most flexibility if you want to start playing around with this data programmatically.

Binding to the folder in EWS

You could use a few different ways to get the TeamChat folder the approach I've used is to make use of the Extended Property TeamChatFolderEntryId which is set on the Root Folder of a Mailbox. This contains the PR_EntryId of the Folder in question so once you convert this to an EWSId using the CovertId operation in EWS you can then bind directly to the folder. eg here's what the code looks like



$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)  
$TeamChatFolderEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([System.Guid]::Parse("{E49D64DA-9F3B-41AC-9684-C6E01F30CDFA}"), "TeamChatFolderEntryId", [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
$psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$psPropset.Add($TeamChatFolderEntryId)
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid,$psPropset)
$FolderIdVal = $null


if ($RootFolder.TryGetProperty($TeamChatFolderEntryId,[ref]$FolderIdVal))
 {
   $TeamChatFolderId= new-object Microsoft.Exchange.WebServices.Data.FolderId((ConvertId -HexId ([System.BitConverter]::ToString($FolderIdVal).Replace("-","")) -service $service))
   $TeamChatFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$TeamChatFolderId);
  }
}

Once you have access to the Folder you can start pulling basic folder statistics like the Number of Items, Sizes, AttachmentCounts etc (or more interestingly things you won't be able to do using Get-MailboxFolderstatitistics). But the more interesting thing to do is to look at the data stored on the conversation items, because these aren't your normal email Messages extended properties are used to hold the different data from Teams/Skype (also the normal Attachment and recipients collections of a message are also used). There are a few properties in use (which generally aren't documented either so if your interested I would suggest taking a look yourself with a MAPI editor at a few items). But the one that is of most interest to me anyway is the SkypeMessagePropertyBag which contains a lot the Metadata from the chat message. This is a string property that contain JSON so accessing and decoding it into something usable in Powershell is pretty easy using ConvertFrom-Json.

I've put together a sample script module for accessing this folder using EWS that has a couple of different functions. The first Get-TeamChatStats accesses the folder and does some simple aggregation of folder and items properties including the ConversationId from the SkypeMessagePropertyBag  to aggregated the number of conversations.


Get-TeamChatSkypeMessagePropertyBag enumerates all the Items in the TeamChat Folder and then outputs the SkypeMessagePropertyBag to the pipeline so you can then do any of you own aggregation or investigation etc eg



My current TeamChat data is pretty simple so there could be a few bugs in these scripts due to the limited data I had to run against. I've put the code up for these script on GitHub at https://github.com/gscales/Powershell-Scripts/blob/master/TeamChatStats.ps1 so fell free to log an issue you experience and I'll path the code.

Viewing all 241 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>