Importing PFDAVAdmin or ExFolders Exports Without PFDAVAdmin or ExFolders

It’s almost May and I haven’t posted anything yet this year, so it’s definitely time to post a new script.

I recently worked with a customer that needed to export the public folder permissions from one Exchange organization and import them into another. The trick was that on the import side, some of the accounts were only mail-enabled – not mailbox-enabled. This creates a problem for ExFolders, because the API it uses does not consider mail-enabled accounts to be valid security principals. However, if you use the Add-PublicFolderClientPermission cmdlet to set the permissions, it has no problem with such an account.

I looked at changing ExFolders to better support this, but the changes would be complex and I just didn’t have that kind of time. Instead, I decided to write a script to import the permissions using the cmdlet. There are a few things to be aware of about the script.

First, the script will be much slower than using PFDAVAdmin or ExFolders to run the import. This is because the cmdlets only let you modify one user at a time, and unless you want to do a lot of complicated calculations, you need to remove the existing permissions for the user before adding the new ones. This means we end up running two cmdlets for every user permission. If you are importing permissions for 5 users on a folder, it will take 10 commands to assign those permissions.

Second, the script currently only works with exports that are in legacyExchangeDN format. If you used NT Account format (domain\user), this script can’t handle them. This means the legacyExchangeDNs from the old environment have to be resolvable in the new one. That is usually the case, since they are brought over as X500 addresses in most migrations.

And finally, this script works only with public folder permissions exports from PFDAVAdmin or ExFolders. If you exported replicas or properties or something, this script won’t help you.

Anyway, here is the script. Enjoy!

# Import-PFPermissions.ps1
#
# The purpose of this script is to import permissions from an export
# file generated using PFDAVAdmin or ExFolders.
#
# Syntax example:
#
# .\Import-PFPermissions C:\someimportfile.txt MYPFSERVER

param([string]$importFile, [string]$server)

function ConvertRightsString([string]$pfdavRightsString)
{
$pfdavRights = $pfdavRightsString.Split(@(' '))
$powershellRights = ""
foreach ($right in $pfdavRights)
{
if ($right -eq "All")
{
$powershellRights += "Owner,"
}
elseif ($right -eq "Owner")
{
$powershellRights += "CreateItems,ReadItems,CreateSubfolders,FolderOwner,FolderVisible,EditOwnedItems,EditAllItems,DeleteOwnedItems,DeleteAllItems,"
}
elseif ($right -eq "Contact")
{
$powershellRights += "FolderContact,"
}
elseif ($right -eq "Create")
{
$powershellRights += "CreateItems,"
}
elseif ($right -eq "CreateSubfolder")
{
$powershellRights += "CreateSubfolders,"
}
elseif ($right -eq "Delete")
{
$powershellRights += "DeleteAllItems,"
}
elseif ($right -eq "DeleteOwn")
{
$powershellRights += "DeleteOwnedItems,"
}
elseif ($right -eq "Write")
{
$powershellRights += "EditAllItems,"
}
elseif ($right -eq "WriteOwn")
{
$powershellRights += "EditOwnedItems,"
}
elseif ($right -eq "o")
{
$powershellRights += "FolderOwner,"
}
elseif ($right -eq "Visible")
{
$powershellRights += "FolderVisible,"
}
elseif ($right -eq "Read")
{
$powershellRights += "ReadItems,"
}
else
{
$powershellRights += ($right + ",")
}
}

$powershellRights = $powershellRights.TrimEnd(@(','))
return $powershellRights
}

function RemoveFolderVisibleWithTrap([string]$folderPath, [string]$server, [string]$userString)
{
trap [Exception]
{
continue
}

$returnedValue = Remove-PublicFolderClientPermission -Identity $folderPath -Server $server -User $userString -AccessRights FolderVisible
}

$fileReader = new-object System.IO.StreamReader($importFile)
while ($null -ne ($buffer = $fileReader.ReadLine()))
{
if ($buffer.StartsWith("SETACL`t"))
{
$columns = $buffer.Split(@("`t"))

# Resolve the folder path, which should always be the second column.
$folderPath = $columns[1]
if ($folderPath.StartsWith("Public Folders\"))
{
$folderPath = $folderPath.Substring(14)
}
elseif ($folderPath.StartsWith("System Folders\"))
{
$folderPath = ("\NON_IPM_SUBTREE" + $folderPath.Substring(14))
}

("Updating folder: " + $folderPath)

for ($x = 2; $x -lt $columns.Length; $x+=2)
{
$userString = $columns[$x]
if ($userString.StartsWith("/o="))
{
$recipient = Get-Recipient $userString
if ($recipient -ne $null)
{
$userString = $recipient.Identity
}
else
{
(" Could not resolve: " + $userString)
continue
}
}
elseif ($userString -ne "Default" -and $userString -ne "Anonymous")
{
(" Skipping unrecognized user format: " + $userString)
continue
}
(" Processing user: " + $userString)
(" Checking for existing permissions...")
$permsForUser = Get-PublicFolderClientPermission -Identity $folderPath -Server $server -User $userString
if ($permsForUser -ne $null)
{
(" Removing existing permissions...")
$permsForUser | Remove-PublicFolderClientPermission -Identity $folderPath -Server $server -Confirm:$false
}

(" Adding new permissions...")
$rightsString = ConvertRightsString $columns[$x+1]
(" AccessRights: " + $rightsString)
$rightsArray = $rightsString.Split(@(','))
if ($permissionString -eq "None")
{
# This requires special handling. In the cmdlet, there is no way to grant a user no permissions,
# denying the ability to even see the folder, because the None role includes the FolderVisible
# right. When a user has None without folder visible, it still shows up as None, but IsRole is
# false. We need to check for that situation here to make sure None is really None. In the
# export, None really means no rights at all.
#
# So, what we do here, is first we set rights of None. Then, we try to remove FolderVisible,
# and ignore any errors that happen. If it succeeds, we don't have FolderVisible anymore, and
# if it fails, we didn't have FolderVisible in the first place, so it doesn't matter.
$returnedValue = Add-PublicFolderClientPermission -Identity $folderPath -Server $server -User $userString -AccessRights $rightsArray
RemoveFolderVisibleWithTrap $folderPath $server $userString
}
else
{
$returnedValue = Add-PublicFolderClientPermission -Identity $folderPath -Server $server -User $userString -AccessRights $rightsArray
}
}
}
}