# Phases I have identified 5 phases of a file server migration with specific steps to help avoid mistakes and give the best end-user experience. This guide is primarily for server to server migrations, however it also applies to a SharePoint migration. ## Phase 1: Info Gathering - File path too long - Permissions - Duplicate file check ## Phase 2: Planning - Determine timeline - Destination file permissions (SharePoint or on-prem) - Copy existing file permissions? - Writing/editing scripts - takeown - robocopy - To successfully use `/MIR`, `/e`, or `/purge` all top-level folders need to have their own script if they come from different sources - If your destination looks like: ``` \\destination\ Finance\ HR\ IT\ ``` Then each robocopy job should target `\\destination\Finance` `\\destination\HR` `\\destination\IT` ### Takeown ```shell takeown /f "D:\Shares" /a /r /d Y ``` - `/a`: Gives ownership to the Administrators group instead of the current user. - `/r`: Performs a recursive operation on all files in the specified directory and subdirectories. - `/d Y`: Suppresses the confirmation prompt that is displayed when the current user does not have the **List Folder** and **Read** permissions on a specified directory, and replaces directory permissions with permission granting the user full control only. ### Robocopy Script This is a draft [[robocopy]] script. I want to edit the script so that no action is taken unless it is run with the `/active` flag. ```PowerShell $SourcePath = "D:\SourceFolder" $DestinationPath = "\\NewFileServer\Share\DestinationFolder" # Safe by default. The script only makes changes when run with /active. $DryRun = $true # Robocopy retries and wait time between retries. $RetryCount = 2 $RetryWaitSeconds = 5 # Include subdirectories, including empty ones. $IncludeEmptyDirectories = $true # Show files and folders that exist only in the destination. $ShowExtraDestinationItems = $true # Delete files and folders that exist only in the destination. # Use with care. If $DryRun is also $true, deletions are only previewed. $DeleteExtraDestinationItems = $false # Logging configuration. $EnableLogging = $true $LogDirectory = "C:\Logs\FileMigration" $LogFilePrefix = "robocopy" # Customize file copy behavior. # Common values: # DAT = Data, Attributes, Timestamps # DATS = Adds NTFS ACLs # COPYALL = Data, Attributes, Timestamps, ACLs, Owner, Auditing $CopyFlags = "DAT" # Additional robocopy switches can be added here if needed. $AdditionalOptions = @( "/Z", "/FFT", "/DCOPY:DA" ) function Test-RobocopySuccess { param ( [int]$ExitCode ) return $ExitCode -lt 8 } $ActiveMode = $args -contains "/active" $DryRun = -not $ActiveMode if ([string]::IsNullOrWhiteSpace($SourcePath)) { throw "SourcePath must be set before running the script." } if ([string]::IsNullOrWhiteSpace($DestinationPath)) { throw "DestinationPath must be set before running the script." } if (-not (Test-Path -LiteralPath $SourcePath)) { throw "Source path does not exist: $SourcePath" } if ($EnableLogging -and -not (Test-Path -LiteralPath $LogDirectory)) { New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null } $robocopyArgs = @( $SourcePath $DestinationPath "*.*" ) if ($IncludeEmptyDirectories) { $robocopyArgs += "/E" } else { $robocopyArgs += "/S" } if (-not $ShowExtraDestinationItems) { $robocopyArgs += "/XX" } if ($DeleteExtraDestinationItems) { $robocopyArgs += "/PURGE" } $robocopyArgs += @( "/COPY:$CopyFlags" "/R:$RetryCount" "/W:$RetryWaitSeconds" "/NP" "/TEE" ) if ($DryRun) { $robocopyArgs += "/L" } if ($EnableLogging) { $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $logFileName = "{0}_{1}.log" -f $LogFilePrefix, $timestamp $logPath = Join-Path -Path $LogDirectory -ChildPath $logFileName $robocopyArgs += "/LOG+:$logPath" } if ($AdditionalOptions.Count -gt 0) { $robocopyArgs += $AdditionalOptions } Write-Host "Starting robocopy job..." Write-Host "Source : $SourcePath" Write-Host "Destination : $DestinationPath" Write-Host "Active mode : $ActiveMode" Write-Host "Dry run : $DryRun" Write-Host "Copy flags : $CopyFlags" Write-Host "Show extras : $ShowExtraDestinationItems" Write-Host "Purge extras: $DeleteExtraDestinationItems" if ($EnableLogging) { Write-Host "Log file : $logPath" } & robocopy @robocopyArgs $robocopyExitCode = $LASTEXITCODE if (Test-RobocopySuccess -ExitCode $robocopyExitCode) { Write-Host "Robocopy completed with exit code $robocopyExitCode." exit 0 } throw "Robocopy failed with exit code $robocopyExitCode." ``` ### Analyze "Extra Files" The output of the robocopy script will specify the total number of files copied, skipped, mismatched, failed, and extras. You can also run this PowerShell command to review the script log file. I want to improve this script to show the full path of affected files. ```PowerShell Select-String "\*EXTRAe" C:\temp\robocopy_diff.txt | Measure-Object ``` ## Phase 3: Staging - Run initial copy job - Testing - Preparing end-users - Stage GPOs or other access to destination files - [[Create SMB shortcut on macOS]] - Update documentation ## Phase 4: Cutover - Check for files that exist only in destination (from renames or deleted files that no longer need to exist) - Read-only existing file shares - Deploy access to destination files - Available for questions/issues ## Phase 5: Wrap up - Finalize documentation - Decomission source files - Follow up with point of contact.