Skip to content

Instantly share code, notes, and snippets.

@robderickson
Last active September 1, 2023 21:44
Show Gist options
  • Save robderickson/ab87cfe11f1de5fe41654676273c8837 to your computer and use it in GitHub Desktop.
Save robderickson/ab87cfe11f1de5fe41654676273c8837 to your computer and use it in GitHub Desktop.
Apply an Exchange retention policy tag to a mailbox folder.
# 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