Site Retention Policies keep sending notification emails to end users even postponed sites

Problem definition:

You are using Site Retention Policies in standard SharePoint 2013/2016 teamsites. The feature works as expected although if site owners extend their teamsite, the site keeps sending notifications about “the site is about to expire and will be deleted”. As it displays already the new deletion date which is one year ahead.

Well, out of the box SharePoint behaviour has been designed that any postpone should be short term and that postpone duration SharePoint keep notify you, even if the site has been postponed.But there is a glich in this design, the site owners can postpone a site over years. Of course in that duration every week (Actually whenever the “Expiration” timer job runs) if you get notification, it will be very annoying.

Luckly we have some workaround to mitigate this.

Before going to workaround, I would like to give some information

How to configure Site Policies, please read following article.
https://support.office.com/en-us/article/use-policies-for-site-closure-and-deletion-a8280d82-27fd-48c5-9adf-8a5431208ba5
“Site Settings -> Site Policies”

Components:

– “Site Policy” Feature: Site Settings -> “Site Collection Features” -> “Site Policy”. It is the feature you should already enabled to able to use “Site Policies”.

– “Expiration Policy” Timer job: Each web application we have one “Expiration” timer job. That timer job has responsible to expire operations.
Enumerates list items and looks for those with an expiration date that has already occurred. For those items, runs disposition processing. Disposition processing most often results in deleting items. But it can perform other actions, such as processing disposition workflows.
https://docs.microsoft.com/en-us/sharepoint/technical-reference/timer-job-reference-for-sharepoint-2013
This timer job’s default schedule is weekly. So you would expect notification emails fires weekly.

-For every SPWeb object ( Root or subwebs) we have; Site Settings -> “Site Closure and Deletion” settings.We have assigning related policies here for a specific SPWeb Object (even root site web object)

– “Project Policy Item List” . This is the hidden list that your policy related items/configurations are stored here.When you assign a policy for a site, it stores here. Every site collection have one of this list instance if you enabled the “Site Policy” Feature. So you can find this list in Site Collection’s root web. (Not under sub webs).

-Also we have several policy system EventRecievers. It is important because we should disable them if we going to play around with policy internal settings.

Let’s give an example.
Assume we have created a new site policy:

“Deletion Event”: Site Created Data + 1 Year .
“Send an email notifications to site owners this far in advance of deletion:” -> 3 Months
“Send follow-up notifications every:” -> 7 Days
“Owners can postpone imminent deletion for”  1 years .

(The below picture say 14 day, consider it 7days pls)
PolicySettings

Current date of the server : 05/01/2017

So what this information tell us:
If we assume we have created a site 05/01/2017 , this site will be deleted on 05/01/2018
Before deletion of 3 months , we start to get notifications. According to our example starting by 05/10/2017. (You will get first notification exactly when the Expiration timerjobs run on that week)

So i have created a brand new subsite and assigned this policy to that subweb from “Site Closure and Deletion”

If we look in “Project Policy List Item” table. We learn more information. You can use below powershell script to get information about related item.
(You need to adjust the script for finding correct list item id, I will not do it here)

If ((Get-PSSnapIn -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{ Add-PSSnapIn -Name Microsoft.SharePoint.PowerShell }

$url = “<your site collection url>”
$rname = “Project Policy Item List”
$site = Get-SPSite $url
$rootweb = $site.OpenWeb()
$rList = $rootweb.Lists[$rname]
$item = $rList.Items[“<please locate the correct item id>”]
write-host “ExpireDate           :” $item[“_dlc_ExpireDate”]
write-host “ProjectExpirationDate:” $item[“ProjectExpirationDate”]
write-host “ProjectCreateDate    :” $item[“ProjectCreateDate”]
write-host “LastRun   :” $item.Properties[“_dlc_LastRun”]
write-host “PROPERTIES”
$item.Properties
write-host “XML”
$item.xml.Replace(“ows_”,[System.Environment]::NewLine + “ows_”)

The result is

ExpireDate           : 05/10/2017 2:27:27 PM  +9 Months.
-This is the value of date, next time “Expiration” timer job notice that should do something about it.
-It is not a real expiration date, it is a changable varible which “Expiration” timer job can calculate and change in time to time.
ProjectExpirationDate: 05/01/2018 2:27:27 PM  +1year.
-This value means when we delete the site according to our formula.
ProjectCreateDate    : 05/01/2017 2:27:27 PM  +0 time.
-This value when we have created the item policy. It is the beginning reference time.

So basically when we pass the date “05/10/2017”, Depends on “Expiration” timer job schedule in this week of the date, the timer job will send the email notification about our site will be deleted in 3 months, Lets assume timer job run on 07/10/2017 (2 days after “ExpireDate” value) your site owners will get first notification about site deletion. Afterwards timer job will update “ExpireDate” value by adding 1 week = 14/10/2017 (based on setting we defined “Send follow-up notifications every”). Also will update/add some other properties like “_dlc_LastRun” it will be 07/10/2017) .

On date 14/10/2017 is the next time “Expiration” timer jobs runs, it will send the second notification about site deletion.Afterwards timer job will update this date adding another 1 week. 21/10/2017. This goes until we reach “ProjectExpirationDate” on that date the object will be deleted. (or closed, depend on how you configure the policy)
Ok. So lets have a look some other important parameters.
ItemRetentionFormula -> It shows the formula of when we start first do something about this record -> For the first time,  “ExpireDate” calculated with this formula.But “ExpireDate” property will be changed afterwards when that date has come by “Expiration” timer job.

PS C:\Users\Administrator.CONTOSO> $item.Properties[“ItemRetentionFormula”]
<formula id=”Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn”><number>-3</number><property>ProjectExpirationDate</property><propertyId></propertyId><period>month
s</period></formula>

Some other important properties.
_dlc_policyId                  0x010085EC78BE64F9478AAE3ED069093B996300ACCF30C2E8DFDE4CB3D2D69F6C58E43C
ows_ProjectWebGuid='{06563951-AC75-4D8A-8835-79907AFE84BB}’
ows_ProjectWebUrl=’http://contososp:9090/sites/corpa/SharePointHub, /sites/corpa/SharePointHub’ -This is my subsite name

ows_ProjectCreateDate  -> We already explained this above
ows_ProjectExpirationDate -> We already explained this above
ows_ProjectIsClosed=’0′ -> If you selected the option close the site before deletion (meaning make it not reacable by users)
ows_ProjectNumberOfPostpone=’0′ -> It will show the number that you can understand any postpone happens.
ws__dlc_ExpireDate-> We already explained this above
ows_ContentType=’MyDeletePolicy’ -> this is same as the Policy name.Well the system works via content type structures behind the scene.

Lets return our problem. The problem begins when your site owner decide and postponed the deletion afterwards he/she got the first notification. Well according to our settings it will be postponed for 1 year ahead. But the problem the owners will continue to get emails for every week (we set “Send follow-up notifications every” it 7 days). I said 🙂 it is annoying.

What happens after you postpone in related item properties in “Project Policy List Item”
ows_ProjectNumberOfPostpone=’1′ -> changed “0” -> “1”
ows_ProjectCreateDate=’05/01/2017 2:27:27′  -> It is not changed.
ows_ProjectExpirationDate=’05/01/2019 2:27:27′ -> but this date increased as 1 year now in 2019 !.
We have new property named
_dlc_ItemStageId=1

 

There is only one workaround, can only applicable with Powershell. if we delete _dlc_LastRun and newly added property _dlc_ItemStageId and run “Expiration” timer job afterwards. Timer job will do a recalculation and correction. It will going to apply again the first time ItemRetentionFormula but this time the ProjectExpirationDate is in 2019.(Remember, it was updated when the site owner postponed). So correction will reset the “ExpireDate” value and you will not get anymore notification emails until  “05/01/2019 minus 3 months”, All Good 🙂

Here is the powershell to fix that issue:

If ((Get-PSSnapIn -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{ Add-PSSnapIn -Name Microsoft.SharePoint.PowerShell }

#Site Collection Level.
$site =get-spsite <site collection url>

# Open the RootWeb
$web = $site.OpenWeb()

#gather  Project Policy Item List hidden list
$list = $web.Lists[“Project Policy Item List”]

#There will be several subsites or different policy item in the list based on usage.
#Need to locate correct policy item in the list with related site or subsite.
#Print all items in that list to find out the relate (site or subsite object)
$list.Items
#Please add your logic based on your requirements.For example you can use ProjectWebGuid or ProjectWebUrl for filter out your related item.I will leave it to you.

#gather the item.
$item = $list.Items[<related item id>]

#-Update procedure for the ExpireDate
#SET EVENT FIRING DISABLED.
$assembly = [Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”);
$type = $assembly.GetType(“Microsoft.SharePoint.SPEventManager”);
$prop = $type.GetProperty([string]”EventFiringDisabled”,[System.Reflection.BindingFlags] ([System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static));
$prop.SetValue($null, $true, $null);

#Update.
$item.Properties.Remove(“_dlc_LastRun”);
$item.Properties.Remove(“_dlc_ItemStageId”);
$item.SystemUpdate($false)

#SET EVENT FIRING ENABLED
$prop.SetValue($null, $false, $null);

+Then run “Expiration” Timer job for related web application.

I strongly suggest, please do some test and practice the script on your test environment before appling it to production!
If you do something incorrect, you can easly messed up your policy items.Or you may call Microsoft Support to help.