A PowerShell Script to Remove Duplicate ACE's from Public Folders

I was talking with a colleague about some of the PowerShell scripts I've written, and he suggested I start sharing them in the blogosphere. So without further delay I'll begin with one that I wrote to help someone with a particularly unpleasant permissions problem with public folders.

The scenario was that somehow the MAPI folder permissions on hundreds of public folders got duplicated when an out-of-sync DC passed incorrect data to Exchange as replicas got added to the organization. Whatever the cause we wound up with a folder that would have, for example, a permission list like this:

Name: Role:
Default None
John Smith Owner
John Smith Owner
Merle Biggs Author
Merle Biggs Author
Anonymous Reviewer

The effective result was that users who should have had access the folder didn't have it. Now we knew that if we deleted the duplicate ACE's then access was restored. The problem was that there were hundreds of folders affected and we needed a way to go through and remove the duplicates automatically.

PowerShell to the rescue!

We set out to clean up the permissions with a script that first captured the existing permissions on a folder and populated a variable with them. We then cleared the entire ACL by removing every permission on the folder. Finally we used the array of permissions originally captured and had PowerShell simply add them back to the folder they came from. The result was that Exchange rejected duplicates when the script attempted to add them, as expected, and so the above example would be corrected to look like this:

Name: Role:
Default None
John Smith Owner
Merle Biggs Author
Anonymous Reviewer

So how did the script work? Here it is, with comments before each section to explain what's happening:

# The GetTreeTop function asks the user for the path to the public folder we want to work on first and puts the result into the $treetop variable
# It then checks to see if the entered path is valid and requests the user to try again if it's not

Function GetTreeTop
{
Write-Host ""
Write-Host ""
$treetop = Read-Host "Enter public folder to reset permissions on?"

#Check validity of public folder path
$check = Get-PublicFolder $treetop -erroraction silentlycontinue
If ($check){
 FindOut
 }
 Else {
 Write-Host ""
 Write-Host "Public folder name $treetop is not valid, please re-enter the path, using format -> FolderSubfolder" -foregroundcolor Red
 Write-Host ""
 GetTreeTop
 }
}

# The FindOut function asks the user if we want to operate just against the one public folder specified in GetTreeTop, or do we want to work against all the subfolders 
as well?
# Based on the Y or N answer we go to either the DoRecurse or the DontRecurse function. If the answer isn't Y or N we ask again.

Function FindOut
{
 $recrse = Read-Host "Do you want to reset permissions on all subfolders beneath this tree? (Y/N)"
 if ($recrse -eq "Y")
 {
 DoRecurse
 }
 elseif ($recrse -eq "N")
 {
 DontRecurse
 }
 else
 {
 FindOut
 }
}

# The DontRecurse function runs Get-PublicFolderClientPermission on the folder that $treetop provides, except where the AccessRights value is "None", and puts the result 
into an array called $perm
# It then removes all the permissions on the folder
# Finally, it runs a ForEach loop with $perm as the input, so that each entry in the array goes through the Add-PublicFolderClientPermission cmdlet, and we put the same 
ACEs back that were present to begin with, minus the duplicates (since Exchange rejects them)

Function DontRecurse
{
Write-Host ""
Write-Host "Removing permissions on public folder $treetop"
Write-Host ""

$perm = get-publicfolderclientpermission $treetop | ?{$_.accessrights -ne "None"}

$perm | remove-publicfolderclientpermission $treetop -confirm:$false

Write-Host ""
Write-Host "Replacing permissions on public folder" $treetop -ForeGroundColor Green
Write-Host ""

$perm | foreach {
 add-publicfolderclientpermission -Identity $_.Identity -user $_.user -accessrights $_.accessrights
 }
Write-Host ""
Write-Host "COMPLETED"
Write-Host ""
exit
}

# The DoRecurse function is the same as the DontRecurse function, except we add create an array of public folder paths based on the original path getting fed to Get-
PublicFolder with the -recurse switch
#We then feed each path into a foreach loop, which itself has a foreach loop just like the DontRecurse function

Function DoRecurse
{
$treepath = get-publicfolder $treetop -recurse -resultsize unlimited
Write-Host ""
Write-Host "Removing permissions on public folder tree $treetop"
Write-Host ""

$treepath | foreach {
 $pf = $_.Identity
 $perm = get-publicfolderclientpermission $pf | ?{$_.accessrights -ne "None"}
 $perm | remove-publicfolderclientpermission $pf -confirm:$false
 Write-Host ""
 Write-Host "Replacing permissions on public folder" $pf -ForeGroundColor Green
 Write-Host ""
 $perm | foreach {
 add-publicfolderclientpermission -Identity $_.Identity -user $_.user -accessrights $_.accessrights
  }
 }
Write-Host ""
Write-Host "COMPLETED"
Write-Host ""
exit
}

# Once all the above functions are defined we call the one we want to execute first:

GetTreeTop