Bill Long's Exchange Blog

Exchange Server stuff, focusing on Public Folders, PFDAVAdmin, ExFolders, and Powershell scripting.

Importing PFDAVAdmin or ExFolders Exports Without PFDAVAdmin or ExFolders

Importing PFDAVAdmin or ExFolders Exports Without PFDAVAdmin or ExFolders

  • Comments 7
  • Likes

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
            }
        }
    }
}

Comments
  • Do you have any script that can export the Mailbox Folder Permission? I am planning to take the permission from one org(exch 2003) to another org (exch 2010) without any trust.

    All your help would be highly appreciated.

  • Hi Aresh,

    Unfortunately, no, I don't have a script for that. You can export the mailbox folder permissions from a 2003 org using PFDAVAdmin, and import them into a 2010 org using ExFolders. The problem is that the mailbox folder path format changed between these two tools. PFDAVAdmin exports using just the alias. ExFolders expects an smtp address. So you can't use the PFAVAdmin export with ExFolders.

    It would be possible to write a script that takes the PFDAVAdmin output and imports it using the Set-MailboxFolderPermission cmdlet, but I don't have one. Maybe I'll get around to writing it one day.

  • Hi,

    im in the same situation as Aresh only that we will have a trust between the Forests.

    Is the alias/smtp-address missmatch the only Problem here? so could i simply replace alias with smtp-address in the Export file before importing?

    Thanks

  • Hi Thorsten,

    Yes, if you can somehow replace all the aliases with SMTP addresses, then the import should work.

  • Hi Bill,

    I have exchange 2003 org and exchange 2010 org. I will export the public folders to pst and import to exchange 2010.

    Is it possible to export exch 2003 public folder permissions using pfdavadmin and import to exch 2010 public folders using exfolders?

    I understand another way would be to use interorg connector, but wondering would export/import perms work without having to setup interorg connector?

    thanks

  • Yes, you should be able to export with PFDAVAdmin from one org and import that file with ExFolders in another org. This is one of the scenarios I had in mind when I wrote ExFolders.

    Note that you have two export formats to choose from. The users can be specified in legacyExchangeDN format or in DOMAIN\user format. In most inter-org migration scenarios, the legacyExchangeDN of the users is preserved as an X500 proxy address, so you'll want to use the legacyExchangeDN format. But if that's not the case, you may need to use DOMAIN\user, and then do a find/replace on the domain name to reflect the domain of the new org.

  • Found one bug, the check [If ($userString.StartsWith("/o=")) ] will fail if the DN in the input file as an uppercase O, as in "/O=".

    Quick fix is to change the previous line to this:

    $userString = $columns[$x].ToLower()

    Just add the ".ToLower()" on the end

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment