Retain Source Folder Permissions While Copying


My last week task was to copy shared folder content from one server to another server. This looks easy but it comes with following conditions:

  • Copy content from Source to target folder( Ignore one folder (DfsPrivate) while copying content from source to target server)
  • Copy source folder permission at all level while copying source to target folder and permission should not be restricted to source root folder. It means each source folder permission must match with target folder.
  • Generate a report to cross verify source and target permission for all folder in source and target.
  • Automate these step, so that it can be executed by end of every month (Should handle delta copy)

I decided to use PowerShell to automate these activities with Robocopy command.

You can download the .ps1 file attached below to skip further reading, however, if you are interested in knowing .ps1 file and wanted to tweak it based on your requirement then you have to read full blog Smile

Following are the methods in the .ps1 file corresponding to the activity mentioned above.


CopyFTPFolder.ps1 file accept three parameter 1) SourceFolderPath 2) TargetFolderPath and 3) Log folder path as shown in above screen.

You can download the .ps1 file and open it in inside Powershell ISE editor.

CopyContent is the method which used robocopy to copy the content from source to target.

Code Snippet

  1. function CopyContent([string] $sourcePath, [string] $targetPath,[string] $logPath)
  2. {        
  3.     $excludeFolderPath = "$sourcePath\DfsPrivate"
  4.     robocopy $sourcePath $targetPath /mir /sec /MT[:32] /XD $excludeFolderPath /Copy:DAT /DCOPY:T /z /np | tee $logPath
  5. }

Here are details of parameter passed to robocopy. You can modify them based on your requirement.

  1.   /mir is used to copy all the content of source to target
  2. /Sec /Copy:DAT /DCOPY:T is used to copy the security, parameter
  3. /XD is used to exclude the copying of “DfsPrivate” folder. 
  4. /MT[:32] will allow powershell script to use 32 thread while performing copying. You can set this value based on your server configuration.
  5.   /Z will be used to perform delta copy.
  6. /np | tee $logPath will be used to log Robocopy activity and also show them on screen, so that user is aware of each activity instead of opening log file.

CopyACL function will copy the “Access Control List” of source folder to target folder.

Code Snippet

  1. function CopyACL($SourceFolderPath, $TargetFolderPath)
  2. {
  3.     #COPY ACL
  4.     Get-Acl $SourceFolderPath | Set-Acl $TargetFolderPath
  5. }

ExportPermission function will iterate through each folder in given path and export ACL permission into csv file

Code Snippet

  1. function ExportPermission($folderPath, $exportPath)
  2. {
  3.     $allFolders = Get-ChildItem $folderPath -Recurse | ? {$_.PSisContainer -eq $true}
  4.     $collection = @()
  5.     foreach ($folder in $allFolders)
  6.     {
  7.       $acl = Get-Acl $folder.FullName
  8.       foreach ($entry in $acl.Access)
  9.       {
  10.         $object = New-Object PSObject -Property @{
  11.         SourcePath=$folder.FullName
  12.         SourceFileSystemRights=$entry.FileSystemRights
  13.         SourceAccessControlType=$entry.AccessControlType
  14.         SourceIdentityReference=$entry.IdentityReference
  15.         SourceIsInherited=$entry.IsInherited
  16.         SourceInheritanceFlags=$entry.InheritanceFlags
  17.         SourcePropagationFlags=$entry.Propagationflags}
  19.         $collection += $object
  20.       }
  21.     }
  22.     $collection | Export-Csv $exportPath
  23. }


Finally the Main method which control the execution of this powershell script and calls above mentioned methods in a sequential order.

Code Snippet

  1. function Main($SourceFolderPath, $TargetFolderPath, $LogPath)
  2. {        
  3.     $timeStamp=(Get-Date).ToString()
  4.     $timeStamp= $timeStamp.Replace("/","").Replace(":","").Replace(" ","")
  5.     $timeLogPath = "$LogPath\scriptLog_$timeStamp.txt"
  6.     $CopyAclLogPath = "$LogPath\CopyACLLog_$timeStamp.txt"
  7.     $exportSourcePermissionPath = "$LogPath\SourcePermission_$timeStamp.csv"
  8.     $exportTargetPermissionPath = "$LogPath\TargetPermission_$timeStamp.csv"
  10.     $robocopyLogPath="$LogPath\RobocopyLog_$timeStamp.txt"
  12.     #Perform Get Set ACL At Root level
  13.     CopyACL $SourceFolderPath $TargetFolderPath
  14.     Write-Host "Copying ACL permission from source to target (Only for root folder)...."
  16.     #Robocopy Operation started
  17.     $startTime = Get-Date
  18.     CopyContent $SourceFolderPath $TargetFolderPath $robocopyLogPath
  19.     $endTime = Get-Date
  20.     $totalTimeTaken = NEW-TIMESPAN  $startTime $endTime
  21.     $str = "Total time taken from $source to $target is $totalTimeTaken"
  22.     Add-Content -Path $timeLogPath $str
  23.     #Completed
  25.     #Perform Get Set for each folder level
  26.     $sourceRootDrive = ([regex]::matches($SourceFolderPath,"[a-zA-Z0-9]:","IgnoreCase")| %{$_.value})
  27.     $targetRootDrive = ([regex]::matches($TargetFolderPath,"[a-zA-Z0-9]+","IgnoreCase")| %{$_.value})[0]
  28.     $targetRootDrive = "\\\cf5 $targetRootDrive"
  30.     Write-Host "sourceRootDrive = $sourceRootDrive"
  31.     Write-Host "targetRootDrive = $targetRootDrive"
  33.     $allFolders = Get-ChildItem $SourceFolderPath -Recurse | ? {$_.PSisContainer -eq $true}
  34.     $collection = @()
  35.     Write-Host "Copy ACL for each folder..."
  36.     foreach ($folder in $allFolders)
  37.     {
  38.         $sourcePath = $folder.FullName        
  39.         $targetPath = $sourcePath.Replace("$sourceRootDrive","$targetRootDrive")     
  41.         CopyACL $sourcePath $targetPath
  43.         Add-Content -path $CopyAclLogPath "'$sourcePath' ------'$targetPath'"
  44.         Write-Output "'$sourcePath' ------'$targetPath'"
  45.         Write-Progress -Activity "sourceRootDrive$ to $targetRootDrive"
  46.     }
  47.     #Completed
  49.     #Export Permission For Source Folder
  50.     Write-Host "Exporting Permission For Source Folder $SourceFolderPath"
  51.     ExportPermission $SourceFolderPath $exportSourcePermissionPath
  52.     #Completed
  54.     #Export Permission For Target Folder
  55.     Write-Host "Exporting Permission For Target Folder $TargetFolderPath"
  56.     ExportPermission $TargetFolderPath $exportTargetPermissionPath
  57.     #Completed
  58. }


 Please fee free to comment and provide your value suggestion.

Note: This script must be executed from Source server only since regex used in Main method expect Drive path (G:\SomeFolderName) for source folder and network path (\\SomeServerName\SomeFolderName) for target path.