An issue came up at my client recently, where our webmaster email account was being flooded with non deliverable email notices. Messages were bouncing back because our SharePoint farm was sending alerts to users and email address that no longer existed.
This was on a WSS 3.0 farm, and we don’t have any third party tools (like Axceler’s ControlPoint) to help manage scenarios like this. So, I decided to tackle the problem with PowerShell.
Of course the first step in writing a PowerShell script is to search the net to see if anyone has already written one. That led me to a couple of useful blog posts/scripts: Check-SharePoint-Orphaned-Users and Delete all alerts for a user in SharePoint. Based on these two posts, I came up with the following script:
1 #For WSS 3.0 or MOSS 2007
2 $12HiveDir = "${env:CommonProgramFiles}\Microsoft Shared\web server extensions\12\"
3 cd $12HiveDir
4 [void][reflection.assembly]::Loadwithpartialname("Microsoft.SharePoint") | out-null
5 [void][reflection.assembly]::Loadwithpartialname("Microsoft.Office.Server.Search") | out-null
6 [void][reflection.assembly]::Loadwithpartialname("Microsoft.Office.Server") | out-null
7
8 $farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
9 $websvcs = $farm.Services | where -FilterScript {$_.GetType() -eq [Microsoft.SharePoint.Administration.SPWebService]}
10 $webapps = @()
11
12 #Change this to match your domain, or even include the OU
13 $LDAP_Domain = "LDAP://DC=contoso,DC=local"
14
15 #From Check-SharePoint-Orphaned-Users: http://sharepointpsscripts.codeplex.com/releases/view/21699
16 function Check_User_In_ActiveDirectory([string]$LoginName, [string]$domaincnx)
17 {
18 $returnValue = $false
19 #Filter on User and NTgroups which only exists
20 $strFilter = "(&(|(objectCategory=user)(objectCategory=group))(samAccountName=$LoginName))"
21 $objDomain = New-Object System.DirectoryServices.DirectoryEntry($domaincnx)
22
23 $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
24 $objSearcher.SearchRoot = $objDomain
25 $objSearcher.PageSize = 1000
26 $objSearcher.Filter = $strFilter
27 $objSearcher.SearchScope = "Subtree"
28
29 #$objSearcher.PropertiesToLoad.Add("name")
30
31 $colResults = $objSearcher.FindAll()
32
33 if($colResults.Count -gt 0)
34 {
35 #Write-Host "Account exists and Active: ", $LoginName
36 $returnValue = $true
37 }
38 return $returnValue
39 }
40
41 #From Delete alerts for a user: http://www.dunxd.com/2010/12/22/delete-all-alerts-for-a-user-in-sharepoint-with-this-powershell-script/
42 function DeleteOrphanedAlerts{
43 foreach ($websvc in $websvcs) {
44 foreach ($webapp in $websvc.WebApplications) {
45 Write-Host "Webapp Name -->"$webapp.Name
46
47 #limit our search to one web app
48 if ($webapp.Name -eq "sharepoint80 - content"){
49 foreach ($site in $webapp.Sites)
50 {
51 # get the collection of webs
52 $webs = $site.AllWebs
53 foreach ($web in $webs)
54 {
55 # get the alerts
56 $alerts = $web.Alerts
57
58 Write-Host " "$web.Title", Alert count "$alerts.Count
59
60 # if more than 0 alerts, iterate through these alerts to see if there is one for the user
61 if ($alerts.Count -gt 0)
62 {
63 $myalerts = @()
64 $mysites += $web.Url
65 foreach ($alert in $alerts)
66 {
67 if ($alert.user.LoginName -ne $null)
68 {
69 if ($alert.user.LoginName.Contains("\"))
70 {
71 $UserName = $alert.User.LoginName.split("\")
72
73 $result = Check_User_In_ActiveDirectory $UserName[1] $LDAP_Domain
74
75 if ($result -eq $false)
76 {
77 echo " Deleting $($alert.Title) alert for $($alert.User.LoginName)"
78 $myalerts += $alert
79 $alertcount++
80 }
81 }
82 }
83 }
84
85 # now we have alerts for this site, we can delete them
86 foreach ($alertdel in $myalerts)
87 {
88 $alerts.Delete($alertdel.ID)
89 }
90 }
91 }
92 }
93 }
94 }
95 }
96 }
97
98 DeleteOrphanedAlerts
99 Write-Host "Completed"
100
This script will loop through all of the Web Applications in the farm, site collections, and webs, looking for all of the alerts. Once an alert is found, it checks to see if the user is still in Active Directory. If not, it adds the alert to a list of alerts to delete.
If you only want it to search through one web application, you can modify line 48 to only continue if the name of the web application matches.
Also note, you’ll need to modify the LDAP path, stored in the $LDAP_Domain variable to match your environment.