Last active
September 1, 2023 21:44
-
-
Save robderickson/ab87cfe11f1de5fe41654676273c8837 to your computer and use it in GitHub Desktop.
Apply an Exchange retention policy tag to a mailbox folder.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# During a migration from Exchange 2010 to Exchange 2016, I needed to transition from Managed Folder Mailbox Policies | |
# to Retention Policies. In an effort to avoid disrupting existing user work flows, I needed to apply a tag to the | |
# Managed Folders folder separate from the DPT. I adapted this script from a blog post by Akash Bhargava (thanks!): | |
# https://docs.microsoft.com/en-us/archive/blogs/akashb/stamping-retention-policy-tag-using-ews-managed-api-1-1-from-powershellexchange-2010 | |
# I'm not sure how well this will work for nested folders. | |
function Add-RetentionTagToFolder { | |
[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')] | |
param( | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='URL for Exchange Web Services (e.g. https://myserver/ews/Exchange.asmx).' | |
)] | |
[string]$EwsUrl, | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='Name of the folder to tag as it appears in Outlook.' | |
)] | |
[string]$FolderName, | |
[Parameter( | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='If the folder is in an Archive mailbox, use this witch.' | |
)] | |
[switch]$IsArchiveFolder, | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='Primary SMTP address of the mailbox containing the folder to tag (the mailbox that will be impersonated).' | |
)] | |
[string]$PrimarySmtpAddress, | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='GUID of the retention policy tag obtained from Get-RetentionPolicyTag | Select-Object Name,GUID' | |
)] | |
[guid]$PolicyTagGuid, | |
[Parameter( | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='PidTagRetentionFlags value for the tag. To find value, apply tag to folder, open the mailbox in MFCMAPI, and find the PR_RETENTION_FLAGS property on the folder.' | |
)] | |
[int]$RetentionFlags = 137, | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage="Age limit in days for the tag. This should match the days defined by the tag's AgeLimitForRetention property. Set to 0 for 'Never Delete'." | |
)] | |
[int]$RetentionPeriod, | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='If enforcing an EwsAllowList in your organization, set this to match a UserAgent string on that list.' | |
)] | |
[string]$UserAgent, | |
[Parameter( | |
Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='Credentials of impersonation account that will be impersonating mailbox specified by the PrimarySmtpAddress parameter.' | |
)] | |
[System.Management.Automation.PSCredential]$Credential, | |
[Parameter( | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='Path to the Exchange Web Services Managed API DLL.' | |
)] | |
[string]$WebServicesPath = "$env:ProgramFiles\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll", | |
[Parameter( | |
ValueFromPipelineByPropertyName=$true, | |
HelpMessage='Path to the Exchange Web Services Managed API authentication DLL.' | |
)] | |
$WebServicesAuthPath = "$env:ProgramFiles\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.Auth.dll" | |
) | |
PROCESS { | |
# NOTE: This function is a re-write of the script found here: https://docs.microsoft.com/en-us/archive/blogs/akashb/stamping-retention-policy-tag-using-ews-managed-api-1-1-from-powershellexchange-2010 | |
# Import the Exchange Web Services Managed API assemblies | |
Import-Module -Name $WebServicesPath | |
Import-Module -Name $WebServicesAuthPath | |
# Prepare credentials | |
if ($Credential.username -like '*\*') { | |
$username = $Credential.username.split('\')[1] | |
$domain = $Credential.username.split('\')[0] | |
} elseif ($Credential.username -like '*@*') { | |
$username = $Credential.username.split('@')[0] | |
$domain = $Credential.username.split('@')[1] | |
} else { | |
Write-Warning 'Unable to determine domain from credentials.' | |
$username = $Credential.username | |
$domain = $null | |
} | |
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.password)) | |
# Setup EWS service | |
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1) | |
$service.Credentials = (new-object Microsoft.Exchange.WebServices.Data.WebCredentials($username, $password, $domain)) | |
$service.url = New-Object Uri($EwsUrl) | |
if ($UserAgent) { | |
$service.UserAgent = $UserAgent | |
} | |
# Impersonate User | |
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$PrimarySmtpAddress) | |
# Find the specified folder | |
$FolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1) | |
$SearchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$FolderName) | |
if ($IsArchiveFolder) { | |
$Results = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::ArchiveMsgFolderRoot,$SearchFilter,$FolderView) | |
} else { | |
$Results = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$SearchFilter,$FolderView) | |
} | |
# Apply the tag | |
if ($Results.TotalCount -eq 0) { | |
Write-Warning "Folder $FolderName does not exist in Mailbox $PrimarySmtpAddress." | |
} else { | |
if ($Force -or $PSCmdlet.ShouldProcess($FolderName,"Setting retention policy tag on mailbox $PrimarySmtpAddress")) { | |
# Create objects for setting the MS-OXPROPS properties for the retention policy tag: PR_POLICY_TAG (0x3019), PR_RETENTION_FLAGS (0x301D), PR_RETENTION_PERIOD (0x301A) | |
## PR_POLICY_TAG: Binary; GUID of the tag you are applying Use Get-RetentionPolicyTag to get the GUID: Get-RetentionPolicyTag 'Tag Name Here' | Select-Object GUID. Reference: https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmsg/4e44a078-c129-45e1-8bf8-cc8026efdca0 | |
## PR_RETENTION_FLAGS: Integer; Apply the tag to a folder and look up the flags with MFCMAPI. Reference: https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmsg/8e03d9d0-0d9d-4620-901c-2343747136eb | |
## PR_RETENTION_PERIOD: Integer; Matches the age limit in days of your tag. 0 never expires. Reference: https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmsg/e2354980-35c0-4984-84b9-afbaf0ca1984 | |
$PidTagPolicyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary) | |
$PidTagRetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer) | |
$PidTagRetentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer) | |
# Set MS-OXPROPS properties on the folder | |
$Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$Results.Folders[0].Id) | |
$Folder.SetExtendedProperty($PidTagRetentionFlags, $RetentionFlags) | |
$Folder.SetExtendedProperty($PidTagRetentionPeriod, $RetentionPeriod) | |
$Folder.SetExtendedProperty($PidTagPolicyTag, $PolicyTagGuid.ToByteArray()) | |
$Folder.Update() | |
} | |
} | |
$service.ImpersonatedUserId = $null | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment