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 https://msdn.microsoft.com/en-us/library/ee217297(v=exchg.80).aspx . 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 https://blogs.technet.microsoft.com/exchange/2018/10/17/disabling-basic-authentication-in-exchange-online-public-preview-now-available/ 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 jcool@datarumble.com -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 gscales@datarumble.com -deletedItems | Group-Object LastActiveParentfolderPath
Or by RetentionDate
Get-LAPFIDItemsFromFolder -MailboxName gscales@datarumble.com -deletedItems | Group-Object RetentionDate | Sort-Object Count -Descending
Show items from a particular LAPFID
Get-LAPFIDItemsFromFolder -MailboxName gscales@datarumble.com -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 gscales@datarumble.com -deletedItems
and you want to restore the first item (which would the also the last item deleted all you need is)
$items[0].move($items[0].LastActiveParentFolder.Id)
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 https://github.com/gscales/Powershell-Scripts/blob/master/LAPFIDModule.ps1 , the Exch-REST Module is available from the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest and GitHub https://github.com/gscales/Exch-Rest
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 gscales@msgdevelop.com(nothing too big or small).
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 https://msdn.microsoft.com/en-us/library/ee217297(v=exchg.80).aspx . 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 https://blogs.technet.microsoft.com/exchange/2018/10/17/disabling-basic-authentication-in-exchange-online-public-preview-now-available/ 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 jcool@datarumble.com -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 gscales@datarumble.com -deletedItems | Group-Object LastActiveParentfolderPath
Or by RetentionDate
Get-LAPFIDItemsFromFolder -MailboxName gscales@datarumble.com -deletedItems | Group-Object RetentionDate | Sort-Object Count -Descending
Show items from a particular LAPFID
Get-LAPFIDItemsFromFolder -MailboxName gscales@datarumble.com -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 gscales@datarumble.com -deletedItems
and you want to restore the first item (which would the also the last item deleted all you need is)
$items[0].move($items[0].LastActiveParentFolder.Id)
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 https://github.com/gscales/Powershell-Scripts/blob/master/LAPFIDModule.ps1 , the Exch-REST Module is available from the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest and GitHub https://github.com/gscales/Exch-Rest
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 gscales@msgdevelop.com(nothing too big or small).