Friday, October 19, 2018

Using the LAPFID (Last Active Parent FolderId) in EWS and the Graph API in PowerShell when reporting on Deleted Items

A little background

The LAPFID property is an extended property that gets set on an Exchange Store Item when its deleted (any type of delete soft or hard) that is the enabler for the original folder item recovery feature that rolled out to both Exchange Online and Exchange OnPrem (2016) last year. If you where to look at this property in a MAPI editor it would look like the following

To understand what that property Id value represents you need to first understand a little bit more about the Folder EntryId format (PR_EntryId) that exchange uses which is documented in this Exchange Protocol Document . A different visual representation of this with the individual components highlighted would look something like this

Hopefully from this you can see that the LAPFID is comprised of the DatabaseGUID and GlobalCounter constituents of the FolderEntryId. To make sense of the LAPFID you need to resolve it back to the actual Mailbox Folder that it represents. A couple of ways you could go about its is to reconstruct the PR_EntryId using the values from the LAPFID and the header values from another folder in the Mailbox which should be the same and then convert that to a EWSId using ConvertId and then Bind to the Id that is returned. The other way which is the method I've used is to enumerate all the Folders in the Mailbox (and Archive store if it exists) and then get the DatabaseGuid + GlobalCounter combinations from the PR_EntryID of those folders, stick this into a Hashtable and then you can look up that for each item to do the resolution process. One issue is that the folder in question in the LAPFID may have been deleted itself which would make both those processes fail.

Getting the LastActiveParentEntryId property in EWS and the Graph API

To get this property value in EWS you need to request the extended property like the following in the EWS Managed API

$LastActiveParentEntryId = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x348a, "Binary")

or if your using the Graph API your request should look something like the following

$expand=SingleValueExtendedProperties($filter=Id eq 'Binary 0x348A'))

Putting them to work in a Script

Apart from the obvious thing of being able to restore items to their original location the LAPFID property can have a variety of reporting uses. I've put together a small module to allow easy access to this property along with some expansion to strongly typed properties so it will show the original folderpath and also the original EWS Folder (if you want to do the restore). I've also done the same for the Graph API in my Exch-Rest module where I've added some cmdlets to view all the deleted and recoverableItems folders and to retrieved this property and do the expansion of the Folder.

Some modifications to the base EWS Script

Because its now more important then ever to stop using Basic authentication for anything see the escalation  I've made changes to the base script I use for EWS and I've now defaulted the Authentication to use oAuth. While if you are using onPrem you may still need to use basic Auth I have provided provision for doing this as well but will require that you use the -basicAuth switch. I also have some different options for oAuth eg if you want to use the ADAL.dll to generate the access token the front-end cmdlet will take the AccessToken via the -AccessToken parameter if nothing is passed in it will use some generic PowerShell methods to generate an Access Token.

Some Examples in Action

The EWS Module has a cmdlet called Get-LAPFIDItemsFromFolder which has three switch's that can be used to access either the DeletedItemsFolder, RecoverableItemsDeletions and  RecoverableItemsPurges. Or you can also use the -FolderPath parameter and pass in a FolderPath if you really like.

eg to get the items form the dumpster's deletions folder use something like

By default only the Primary Mailbox of the user you enter is checked but if you want to also check the Archive you can use the Archive switch eg

Get-LAPFIDItemsFromFolder -MailboxName -recoverableItemsDeletions -Archive | select LastActiveParentfolderPath,Subject,Size

Or maybe you want to see how many items from each LAPFID folder in the DeletedItems folder

Get-LAPFIDItemsFromFolder -MailboxName -deletedItems | Group-Object LastActiveParentfolderPath

Or by RetentionDate

Get-LAPFIDItemsFromFolder -MailboxName -deletedItems | Group-Object RetentionDate | Sort-Object Count -Descending

Show items from a particular LAPFID

Get-LAPFIDItemsFromFolder -MailboxName -deletedItems | Where-Object {$_.LastActiveParentFolderPath -eq "\Junk E-mail"} | select DateTimeReceived,Subject,Size

To Restore an item to its original folder you just use the normal EWS Move operation, because I'm using the EWS Managed API there is a Move method on Each object that can be called so you just need to pass in the folderId of the folder you want to move it to. In the case the original folder you can just pass in the expanded value. eg if I get a collection of items like

$items =  Get-LAPFIDItemsFromFolder -MailboxName -deletedItems

and you want to restore the first item (which would the also the last item deleted all you need is)


this would move the items back to its LastActiveParentFolder value.

Exch-REST and the Graph API

The Graph API has a few restrictions around Deleted Items when compared against EWS eg because the Deleted Items access method uses the Mail Endpoint you will only be able to get Items with a ItemClass (or Subclass) of IPM.Note. So while other things like contacts and appointments maybe in these folder these items won't show in the API. Simular with Folders if the folder doesn't have a Class or Subclass of IPF.Note it won't show. And lastly you can't currently access the Archive using the Graph API. (but apart from that)

In the Exch-Rest module there a 3 cmdlets that can be used to return the information

Because each of the cmdlets in this module build on each other you can do some interesting things like process the Antispam header to get the DMARC and DKIM information and show that along with the LAPFID eg

 Get-EXRDeletedItems -ReturnLastActiveParentFolderPath  -ReturnInternetMessageHeaders -ProcessAntiSPAMHeaders | select LastActiveParentFolderPath,DMARC,DKIM

Or the Sentiment property to see if people are deleting negative email eg

Get-EXRDeletedItems -ReturnLastActiveParentFolderPath  -ReturnSentiment | select LastActiveParentFolderPath,Sentiment,Subject | where-object {$_.Sentiment -ne $null}

The EWS PowerShell Module for this can be found on GitHub Here , the Exch-REST Module is available from the PowerShell Gallery and GitHub

Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365,Microsoft Teams, Exchange or Active Directory related development work or scripting, please contact me at too big or small).


Unknown said...

This is really very informative

Eriq VanBibber said...


I've used the GlobalCounter value for years to uniquely track items and folders.

however, i've never really understood the "Database Guid" value from the protocol docs.

are you able to provide any better insight in that regard? for example, is there anyway to tie the guid back to a specific database?
if a mailbox was moved to another database, with those guids change?
if a mailbox was made active on a different database copy (think DAG), would it have the same database guid?

or, is the database guid actually a mailbox identifier? this would seem more likely because it would tie the counter to that specific mailbox and allow easier counter reuse in other mailboxes.