Stamping archive tag on specific folder via EWS RRS feed

  • Question

  • Hi,

    I'm configuring an retentiontag for archiving a specific folder after 2 days.
    I'm performing this via EWS.

    The goal is to stamp a specific folder (called "TESTFOLDER" that's available within every mailbox), with a personal tag I created.

    So far I've modified the script I've found here :

    1) created a personal tag called "1_Day_move_to_Archive"
    2) Changed some settings of the code (EWS version, paths, etc.)
    3) configured my account with impersonation rights. (to my own mailbox)
    4) Inserted the GUID of the created tag into the script
    5) Changed this value "$oFolder.SetExtendedProperty($RetentionFlags, 16)" to "$oFolder.SetExtendedProperty($RetentionFlags, 8)"
    16 specifies ExplicitArchiveTag, 8 specifies personaltag >> I tried both settings here

    Now when I run the script I don't get any errors thrown, and I get the prompt that the policy is stamped.
    When I check afterwards via outlook/owa, I don't see any change however.

    I already forced the folder assistant to run, but it also doesn't change anything.

    Anyone knows what might be wrong?



    # The script requires the EWS managed API, which can be downloaded here:
    # http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1
    # This also requires PowerShell 2.0
    # Make sure the Import-Module command below matches the DLL location of the API.
    # This path must match the install location of the EWS managed API. Change it if needed.

    [string]$info = "White"                # Color for informational messages
    [string]$warning = "Yellow"            # Color for warning messages
    [string]$error = "Red"                 # Color for error messages
    [string]$LogFile = "C:\Temp\Log.txt"   # Path of the Log File

    function StampPolicyOnFolder($MailboxName)
     Write-host "Stamping Policy on folder for Mailbox Name:" $MailboxName -foregroundcolor $info
     Add-Content $LogFile ("Stamping Policy on folder for Mailbox Name:" + $MailboxName)

    #Change the user to Impersonate
     $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);

     #Search for the folder you want to stamp the property on
     $oFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
     $oSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$FolderName)
     $oFindFolderResults = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$oSearchFilter,$oFolderView)

    if ($oFindFolderResults.TotalCount -eq 0)
           Write-host "Folder does not exist in Mailbox:" $MailboxName -foregroundcolor $warning
           Add-Content $LogFile ("Folder does not exist in Mailbox:" + $MailboxName)
           Write-host "Folder found in Mailbox:" $MailboxName -foregroundcolor $info

           #PR_ARCHIVE_TAG 0x3018 – We use the PR_ARCHIVE_TAG instead of the PR_POLICY_TAG
           $ArchiveTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3018,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);

           #PR_RETENTION_FLAGS 0x301D
           $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

           #PR_ARCHIVE_PERIOD 0x301E - We use the PR_ARCHIVE_PERIOD instead of the PR_RETENTION_PERIOD
           $ArchivePeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301E,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

           #Change the GUID based on your policy tag
           $ArchiveTagGUID = new-Object Guid("{7d71ddd7-061a-473e-8b92-033e126bbcb0}");

           #Bind to the folder found
           $oFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$oFindFolderResults.Folders[0].Id)

           #Same as that on the policy - 16 specifies that this is a ExplictArchiveTag -- EDIT (also tried with value 8 > personal tag)
           $oFolder.SetExtendedProperty($RetentionFlags, 16)

           #Same as that on the policy - Since this tag is disabled the Period would be 0 -- We choose 1, since we want to archive after 1 day
           $oFolder.SetExtendedProperty($ArchivePeriod, 2)


           Write-host "Retention policy stamped!" -foregroundcolor $info
           Add-Content $LogFile ("Retention policy stamped!")

     $service.ImpersonatedUserId = $null

    #Change the name of the folder. This is the folder the properties will be stamped on.
    $FolderName = "TESTFOLDER"

    Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"

    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

    # Set the Credentials
    $service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("MyAccount","MyPassword","MyDomain")

    # Change the URL to point to your cas server
    $service.Url = new-object Uri("https://owa.customer.int/EWS/Exchange.asmx")

    # Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
    $UseAutoDiscover = $false

    #Read data from the UserAccounts.txt.
    #This file must exist in the same location as the script.

    import-csv UserAccounts.txt | foreach-object {
        $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

        if ($UseAutoDiscover -eq $true) {
            Write-host "Autodiscovering.." -foregroundcolor $info
            $UseAutoDiscover = $false
            $service.AutodiscoverUrl("$WindowsEmailAddress", {$true})
            Write-host "Autodiscovering Done!" -foregroundcolor $info
            Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

        #To catch the Exceptions generated
        trap [System.Exception]
            Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
            Add-Content $LogFile ("Error: " + $_.Exception.Message);


    Wednesday, November 26, 2014 12:48 PM

All replies

  • Generally the best method you can use to test this is set it first manually on a Folder with Outlook/OWA and then use a script to read the properties on the folder. This will give you the correct PolicyGUID, RetentionFlags etc to use in your script (depending how you have read the GUID it may need to be transposed). Then try the same setting on a folder where you haven't set the policyTag and that should work.


    Thursday, November 27, 2014 5:21 AM
  • Hi Glen!

    Thanks a lot for your reply!

    I already checked that.
    What I did notice that in my case the code 0x3018 had some trailing characters (when I checked via MFCMAPI)
    When changing it in the script, and running it, I had some content conversion errors.

    Maybe there has been a change in one of the following CU's/SP's

    I'll see if I can find some more info

    Wednesday, December 3, 2014 1:29 PM
  • I've tested some more, and the value I get when I manually stamp it via outlook is :
    Tag: 0x301D0003
    Property Name(s): PR_RETENTION_FLAGS, PidTagRetentionFlags, ptagRetentionFlags

    When I use this value in the script like this  :

    #PR_RETENTION_FLAGS 0x301D --- EDIT : changed to 0x301D0003
    $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D0003,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    # original $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

    I get this error when executing :
    Error: Exception calling ".ctor" with "2" argument(s): "The extended property tag value must be in the range of 0 to 65,535.

    Parameter name: tag"

    So it indeed looks like a content conversion issue.
    I tried using the value "long" instead of "Integer" but the problem remains.



    PS : I do see this value

    Wednesday, December 3, 2014 3:17 PM
  • The 0003 in the Property Tag is the DataType which refers to Integer type in EWS you only use the first 4 digit of the Tag and you specify the DateType enum.

    What version of Exchange are you using on 2013 you can just use the Strongly typed property.  The most likely issue you having is the GUID you using is wrong I would suggest you grab the hexvalue of the GUID using the MAPIEditor and use that eg the following works okay for me. I've also put in a 2013 example

    ## Get the Mailbox to Access from the 1st commandline argument
    $MailboxName = $args[0]
    $EWSDLL = (($(Get-ItemProperty -ErrorAction SilentlyContinue -Path Registry::$(Get-ChildItem -ErrorAction SilentlyContinue -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Web Services'|Sort-Object Name -Descending| Select-Object -First 1 -ExpandProperty Name)).'Install Directory') + "Microsoft.Exchange.WebServices.dll")
    if (Test-Path $EWSDLL)
        Import-Module $EWSDLL
        "$(get-date -format yyyyMMddHHmmss):"
        "This script requires the EWS Managed API 1.2 or later."
        "Please download and install the current version of the EWS Managed API from"
        "Exiting Script."
    ## Set Exchange Version  
    $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1  
    ## Create Exchange Service Object  
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)  
    ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials  
    #Credentials Option 1 using UPN for the windows Account  
    $psCred = Get-Credential  
    $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())  
    $service.Credentials = $creds      
    #Credentials Option 2  
    #service.UseDefaultCredentials = $true  
    ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates  
    ## Code From http://poshcode.org/624
    ## Create a compilation environment
    $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
    $Params=New-Object System.CodeDom.Compiler.CompilerParameters
    $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
      namespace Local.ToolkitExtensions.Net.CertificatePolicy{
        public class TrustAll : System.Net.ICertificatePolicy {
          public TrustAll() { 
          public bool CheckValidationResult(System.Net.ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert, 
            System.Net.WebRequest req, int problem) {
            return true;
    ## We now create an instance of the TrustAll and attach it to the ServicePointManager
    ## end code from http://poshcode.org/624
    ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use  
    #CAS URL Option 1 Autodiscover  
    "Using CAS Server : " + $Service.url   
    #CAS URL Option 2 Hardcoded  
    #$uri=[system.URI] "https://casservername/ews/exchange.asmx"  
    #$service.Url = $uri    
    ## Optional section for Exchange Impersonation  
    #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName) 
    function hex2binarray($hexString){
        $i = 0
        [byte[]]$binarray = @()
        while($i -le $hexString.length - 2){
            $strHexBit = ($hexString.substring($i,2))
            $binarray += [byte]([Convert]::ToInt32($strHexBit,16))
            $i = $i + 2
        return ,$binarray
    function FolderIdFromPath{
    	param (
    	        $FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )"
    		## Find and Bind to Folder based on Path  
    		#Define the path to search should be seperated with \  
    		#Bind to the MSGFolder Root  
    		$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)   
    		$tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
    		#Split the Search path into an array  
    		$fldArray = $FolderPath.Split("\") 
    		 #Loop through the Split Array and do a Search for each level of folder 
    		for ($lint = 1; $lint -lt $fldArray.Length; $lint++) { 
    	        #Perform search based on the displayname of each folder level 
    	        $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) 
    	        $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint]) 
    	        $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView) 
    	        if ($findFolderResults.TotalCount -gt 0){ 
    	            foreach($folder in $findFolderResults.Folders){ 
    	                $tfTargetFolder = $folder                
    	            "Error Folder Not Found"  
    	            $tfTargetFolder = $null  
    		if($tfTargetFolder -ne $null){
    			return $tfTargetFolder.Id.UniqueId.ToString()
    #Example use
    $fldId = FolderIdFromPath -FolderPath "\Inbox\wwwww"
    $SubFolderId =  new-object Microsoft.Exchange.WebServices.Data.FolderId($fldId)
    $SubFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SubFolderId)
    # Set Tag 2010
    $RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
    $ArchiveTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3018,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
    $ArchivePeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301E,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
    $SubFolder.SetExtendedProperty($ArchiveTag,(hex2binarray "6F4D906673BF60449ABE8EBFAD894A0F"));
    ##2013 use StronglyType Property
    $getRTResp = $service.GetUserRetentionPolicyTags();
    foreach ($rtTag in $getRTResp.RetentionPolicyTags) {
        if ($rtTag.DisplayName -eq "Personal 1 year move to archive")
    			  $SubFolder.ArchiveTag = New-Object Microsoft.Exchange.WebServices.Data.ArchiveTag($true,$rtTag.RetentionId)              

    Thursday, December 4, 2014 5:58 AM
  • D_Goossens, did you ever get this working?

    I need to accomplish the same test and I am using your above script but I get nothing at all

    Tuesday, February 12, 2019 9:02 PM
  • Hi, Did you get this working. I'm trying to do the same thing.  I can see after the script runs the property is stamped, but it seems that it is removed shortly after.  My thought is that it may have to do whether the archive personal tag is included in the retention policy applied to the user, or not.  I'm still testing, but I don't want to expose the tag to the end users. 


    Wednesday, March 20, 2019 10:33 PM