Applying Retention Tags To Calendar Folder - David-Barrett-MS/PowerShell-EWS-Scripts GitHub Wiki
This is a fully worked example showing how to apply retention tags to the Calendar folder using EWS. This sample uses the Update-Folders.ps1 script.
To access EWS, an application needs to be registered in Azure AD and EWS permissions assigned to that app. As the intention is to access multiple mailboxes, Application EWS.AccessAsApp permission is required.
This example assumes that an application is registered in Entra AD with EWS permisssion as described in Exchange Online OAuth Configuration.
Create retention label. I created one called "Keep forever".
Create the retention policy by following steps 1 and 2 from https://learn.microsoft.com/en-us/exchange/security-and-compliance/messaging-records-management/create-a-retention-policy
I created a policy called "Calendar Retention Policy" configured to retain items forever.
If you are creating new policies it can take some time (up to a week per the docs) for these to appear in client mail apps.
The easiest way to retrieve the property values that need to be applied to configure a specific policy is to set that policy on a test folder and then read the properties that the client created.
Open a mail client and apply the policy to a new folder. For Outlook on the Web, the process is:
- Create the new folder (I created a folder called Retention Test under the Inbox).
- Right-click the new folder, choose Assign Policy, and then the appropriate policy.
Now that the policy is applied to the folder, we need to read the MAPI retention properties that were set on the folder so that we can set those properties on other folders (in this example, the Calendar folder). MFCMapi is a useful tool for reading MAPI properties directly from the mailbox, but in this example I am going to use SOAPe so that we can retrieve the property values in a format directly usable by EWS. The properties we need to read are PidTagPolicyTag, PidTagRetentionFlags, PidTagRetentionPeriod.
- Open SOAPe.
- On SOAP URL tab, select Office 365.
- On Authentication tab, click OAuth Settings to open the OAuth configuration window. Fill in the required information (using an app auth flow, e.g. secret key) - you can use the same application that is set up for running the script. When you click Acquire (to authenticate and acquire a token), you should see the token appear in the Token textbox in the main SOAPe window. If an error occurs, the error response will be shown there instead. When a token is successfully obtained, close the auth config window and move on to the next step.
- On EWS Header tab, enter the SMTP address of the test mailbox, and choose Primary SMTP Address as the identifier in the drop-down. Set the Exchange version to Exchange2016.
The final step before being able to apply the retention policy to other folders is to retrieve the property values from the test folder.
- From SOAPe main window, click Load from Template.
- In the drop-down, select FindFolder to load that template (if the Load button is highlighted you may need to click that to load - this happens if another template is currently loaded).
- For FolderId, enter Inbox (this is because I created my Retention Test folder under the Inbox). Click Apply to send the template to the main window.
- From the main window, click Send.
- You should see a response that contains all subfolders of the Inbox. In my case, I have a single folder which is the folder I want:
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion="15" MinorVersion="20" MajorBuildNumber="8699" MinorBuildNumber="26" Version="V2018_01_08" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</s:Header>
<s:Body>
<m:FindFolderResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:FindFolderResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:RootFolder TotalItemsInView="1" IncludesLastItemInRange="true">
<t:Folders>
<t:Folder>
<t:FolderId Id="AAMkADJlMzMwYzVkLWU2MTMtNDY1My1hZWY5LTNhMTNhM2E0NDI2MwAuAAAAAADp0gBRG2/lRYcRjZ+OMrxjAQBZX8/x3RCDTrR+14XGL/QGAAAHQ91RAAA=" ChangeKey="AQAAABYAAABZX8/x3RCDTrR+14XGL/QGAAASBXSa" />
<t:DisplayName>Retention Test</t:DisplayName>
<t:TotalCount>0</t:TotalCount>
<t:ChildFolderCount>0</t:ChildFolderCount>
<t:UnreadCount>0</t:UnreadCount>
</t:Folder>
</t:Folders>
</m:RootFolder>
</m:FindFolderResponseMessage>
</m:ResponseMessages>
</m:FindFolderResponse>
</s:Body>
</s:Envelope>
- Now we have the FolderId (in SOAPe, right-click that and select Copy ItemId (Id only)), we perform a GetFolder to retrieve the retention properties.
- From SOAPe main windows, click Load Template (or use the existing template window if still open).
- Choose the GetFolder template from the drop-down (click Load if necessary).
- Paste the FolderId copied from the FindFolder response.
- Click Apply to send this template to the main window.
- In the SOAPe main window, we need to add the retention ExtendedProperties that we want to retrieve. This can be done by right-clicking the Request window and choosing Add Extended Property (for each required property), or by editing the request directly. This is the request based on the response above (note that these values will change depending upon the policy applied):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<Header xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<RequestServerVersion Version="Exchange2016" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
<ExchangeImpersonation xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
<ConnectingSID>
<PrimarySmtpAddress>[email protected]</PrimarySmtpAddress>
</ConnectingSID>
</ExchangeImpersonation>
</Header>
<soap:Body>
<GetFolder xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<FolderShape>
<t:BaseShape>Default</t:BaseShape>
<AdditionalProperties xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
<ExtendedFieldURI PropertyTag="12313" PropertyType="Binary" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
<ExtendedFieldURI PropertyTag="12317" PropertyType="Integer" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
<ExtendedFieldURI PropertyTag="12314" PropertyType="Integer" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</AdditionalProperties>
</FolderShape>
<FolderIds>
<t:FolderId Id="AAMkADJlMzMwYzVkLWU2MTMtNDY1My1hZWY5LTNhMTNhM2E0NDI2MwAuAAAAAADp0gBRG2/lRYcRjZ+OMrxjAQBZX8/x3RCDTrR+14XGL/QGAAAHQ91RAAA=" />
</FolderIds>
</GetFolder>
</soap:Body>
</soap:Envelope>
- The response contains the values that we need to be able to run the script in the next step. The only part of the response we are interested in is the ExtendedProperties element e.g.
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="0x3019" PropertyType="Binary" />
<t:Value>TxyAvxdrvU+oLxn4ZTujIg==</t:Value>
</t:ExtendedProperty>
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="0x301d" PropertyType="Integer" />
<t:Value>137</t:Value>
</t:ExtendedProperty>
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="0x301a" PropertyType="Integer" />
<t:Value>0</t:Value>
</t:ExtendedProperty>
Now that we have the retention properties that apply the policy we want, we need to apply the same to any other folder. This can be done with the Update-Folder.ps1 script using the -AddFolderProperties parameter to specify the properties and their values.
This can be done by setting relevant variables to pass to the script (this makes further automation easier e.g. running against multiple mailboxes):
$retentionPolicyProperties = @{"0x30190102" = 'TxyAvxdrvU+oLxn4ZTujIg=='; "0x301D0003" = 137; "0x301A0003" = 0 }
$foldersToApplyPolicyTo = @("WellKnownFolderName.Calendar", "WellKnownFolderName.Tasks")
$tenantId = "TENANT ID"
$clientId = "CLIENT/APPLICATION ID"
$secretKey = "CLIENT SECRET KEY"
$mailbox = "MAILBOX SMTP ADDRESS"
Once the variable are set:
.\Update-Folders.ps1 -Mailbox $Mailbox -FolderPaths $foldersToApplyPolicyTo -Office365 -AddFolderProperties $retentionPolicyProperties -OAuth -OAuthTenantId $tenantId -OAuthClientId $clientId -OAuthSecretKey $secretKey