Exporting and Importing file shares from one Windows Server to another

Have you ever found yourself in an environment where there is hundreds of different file shares with different permissions all nested pretty deeply within each other? Where you absolutely had to migrate that file server to another one and also preserve this (less than ideal) structure? If you haven't well, that's good for you. But if you did and had to recreate all shares manually, well, I feel for you.

I made this extremely basic script to export file shares from one server and import them in another one. Keep in mind, this might be buggy, but it might also save you a lot of time. Keep in mind this does not copy the data, it needs to be already there, I personally recommend FreeFileSync.

You'll need two scripts, they are available on my github; Export and Import. Go check them out to get a sense of how they work.

First; Export

Take this given situation, you have a file server with shares on the C: and D: drives, and you want all shares to point to drive D:. You'll need to modify the beginning of the script accordingly:

## This would be the way to do it.
$OriginalDrives = @('C:\', 'D:\')
$TargetDrives = @(
    [pscustomobject]@{
        Original = 'C:\'
        Target = 'D:\'
    },
    [pscustomobject]@{
        Original = 'D:\'
        Target = 'D:\'
    }
)

Now let's say you have shares on the C:, D: and F: drives, but you want all shares on C: and D: to go to D: and all shares on F: to go to C:, this is how you do it:

## This would be the way to do it.
$OriginalDrives = @('C:\', 'D:\', 'F:\')
$TargetDrives = @(
    [pscustomobject]@{
        Original = 'C:\'
        Target = 'D:\'
    },
    [pscustomobject]@{
        Original = 'D:\'
        Target = 'D:\'
    },
    [pscustomobject]@{
    	Original = 'F:\'
        Target = 'C:\'
    }
)

Pretty easy. Let me explain how this scripts work farther than this.

The next thing this script does is get all the share from Windows, this is accomplished easily:

$Shares = Get-SmbShare | Where {($_.Name -notlike 'ADMIN$') -or ($_.Name -notlike 'IPC$') -or ($_.Name -notlike 'C$')}
$SharePermMap = @()

You'll find that we specifically exclude some windows default shares, such as ADMIN$, IPC$ and C$. Oh yeah, I don't recommend running this on a domain controller at all. We also initialize the object that will return all our permissions.

Next, we'll iterate over each share to get the permissions contained within. We specifically want both ALLOW and DENY permissions. Event though using DENY is not part of the best practices, I'm betting if you're in need of this script, there'll be a few denies here and there.

First, we start our for loop and then initialize our ShareObject

foreach ($s in $Shares) {
    $ShareObject = [pscustomobject]@{
        ConvertedPath = '' ## This will be the resultant path converted from what we setup at the start of the script
        PermissionsAllow = [pscustomobject]@{
            Full = @()
            Change = @()
            Read = @()
        }
        PermisisonsDeny = [pscustomobject]@{
            Full = @()
            Change = @()
            Read = @()
        }
    }
[...]

Once we've got that, we can move on to converting the paths using the "map" we created at the start

[...]
    foreach ($d in $OriginalDrives) {
    
        if ($s.Path -like ($d + '*')) {
            $ShareObject.ConvertedPath = $s.Path.Replace($d, ($TargetDrives | Where {$_.Original -eq $d}).Target)
            
        }
	}
[...]

This is one place where PowerShell truly shines in my opinion, those inline pass the object statements are very nice.

Next, we export the permissions:

$Perm = $s | Get-SmbShareAccess 
    
    foreach ($p in $Perm) {
        if ($p.AccessControlType -eq 'Allow') {
            $PermType = [string]$p.AccessRight
            $ShareObject.PermissionsAllow.$PermType = $ShareObject.PermissionsAllow.$PermType + [string]$p.AccountName
        } elseif ($p.AccessControlType -eq 'Deny') {
            $PermType = [string]$p.AccessRight
            $ShareObject.PermissionsDeny.$PermType = $ShareObject.PermissionsDeny.$PermType + [string]$p.AccountName
        } else {
            Write-Error 'Unhandled Access Control Type: ' + $PermType + ' for user: ' + $p.AccountName + ' on share ' + $s.Name
        
        }
    }

This is easy enough, we only handle Allow and Deny permissions, if it's something else we throw an error.

Then, we'll convert the $ShareObject into something more CSV Friendly, we'll also add it to the $SharePermMap we declared initially:

$FinalObject = [pscustomobject]@{
        Name = $s.Name
        Description = $s.Description
        OriginalPath = $s.Path
        ConvertedPath = $ShareObject.ConvertedPath
        Permissions = [pscustomobject]@{
            Full = ($ShareObject.PermissionsAllow.Full -join ',')
            Change = ($ShareObject.PermissionsAllow.Change -join ',')
            Read = ($ShareObject.PermissionsAllow.Read -join ',')
        }
        PermissionsDeny = [pscustomobject]@{
            Full = ($ShareObject.PermissionsDeny.Full -join ',')
            Change = ($ShareObject.PermissionsDeny.Change -join ',')
            Read = ($ShareObject.PermissionsDeny.Read -join ',')
        }
    }

    $SharePermMap = $SharePermMap + $FinalObject

That's it for our initial statement for statement. All you'll see after that is a Save-File block to open a nice dialog to save the file and an Export-Csv statement.


Finally: Importing

Importing is easy, you only need to change two lines in the script:

$PathToCsv = ""
$PathToErrorsCsv = ""

[...]

$PathToCsv being the path to the CSV generated by the previous script, and the $PathToErrorsCsv being to path to the new csv with all encountered errors.

This will check for already existing file shares, but it may not work. It is paramount that the folder structure be already there and you should run the PowerShell process as SYSTEM.