NTFSPermissionMigration.psm1

$errorsLogFilePath = 'C:\PermissionErrors.txt'

function Save-Acl
{
    <#
        .SYNOPSIS
            This function uses icacls to recursively retrieves all permissions from all files and folders in a particular
            folder path and saves them to a text file.
    
        .EXAMPLE
            PS> Save-Acl -FolderPath \\FILESERVER\FileShare -SaveFilePath C:\FileSharePermissions.txt
            
        .PARAMETER FolderPath
             A Mandatory string parameter representing a valid and accessible folder path. This can be a UNC path
             or a local path.

        .PARAMETER SaveFilePath
             A mandatory string parameter representing a path to save the text file full permissions to. This will be
             referenced with the Restore-Acl function later.
    #>

    [OutputType([void])]
    [CmdletBinding()]
    param
    (   
        [Parameter(Mandatory)]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [string]$FolderPath,

        [Parameter(Mandatory)]
        [string]$SaveFilePath
    )
    begin
    {
        $ErrorActionPreference = 'Continue'
    }
    process
    {
        try
        {
            Invoke-ICacls @PSBoundParameters | ForEach-Object {
                Write-Output $_
            }
            Get-Content -Path $errorsLogFilePath | foreach { Write-Error -Message $_ }
        }
        catch
        {
            $PSCmdlet.ThrowTerminatingError($_)
        } finally {
            Remove-Item -Path $errorsLogFilePath -ErrorAction SilentlyContinue
        }
    }
}

function Restore-Acl
{
    <#
        .SYNOPSIS
            This function takes the file generated by Save-Acl and applies all permissions therein to a mirror of the folder
            structure that was originally read. For this to work, the folder structure originally read by Save-Acl must
            be exactly the same as the one you're pointing to here.
    
        .EXAMPLE
            PS> Restore-Acl -RestoreToFolderPath \\NEWFILESERVER\FileShare -PermissionFilePath C:\FileSharePermissions.txt

        .PARAMETER RestoreToFolderPath
             A mandatory string parameter representing the folder path that exactly mirrors the folder structure that was
             read with Save-Acl.

        .PARAMETER PermissionFilePath
             A mandatory string parameter representing the text file saved by Save-Acl.
    
    #>

    [OutputType()]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [string]$RestoreToFolderPath,

        [Parameter(Mandatory)]
        [ValidateScript({Test-Path -Path $_ -PathType Leaf})]
        [string]$PermissionFilePath
    )
    try {
        Invoke-ICacls -FolderPath $RestoreToFolderPath -RestoreFilePath $PermissionFilePath | ForEach-Object {
            Write-Output $_
        }
        Get-Content -Path $errorsLogFilePath | foreach { Write-Error -Message $_ }
    } catch {
        $PSCmdlet.ThrowTerminatingError($_)
    } finally {
        Remove-Item -Path $errorsLogFilePath -ErrorAction SilentlyContinue
    }
    
}

function Invoke-ICacls
{
    [OutputType()]
    [CmdletBinding()]
    param
    (
        [Parameter()]
         [ValidateScript({Test-Path -Path $_ -PathType Container})]
        [string]$FolderPath,

        [Parameter(ParameterSetName = 'Save')]
        [string]$SaveFilePath,

        [Parameter(ParameterSetName = 'Restore')]
         [ValidateScript({Test-Path -Path $_ -PathType Leaf})]
        [string]$RestoreFilePath
    )

    if ($PSCmdlet.ParameterSetName -eq 'Save') {
        icacls "$FolderPath\*" /save $SaveFilePath /t /c 2>$errorsLogFilePath
    } elseif ($PSCmdlet.ParameterSetName -eq 'Restore') {
        icacls $FolderPath /restore $RestoreFilePath /c 2>$errorsLogFilePath
    }   
}