Private/Move-UserData.ps1

<#
.SYNOPSIS
Handles the migration of user data, optionally for a specific folder.
 
.DESCRIPTION
Uses Robocopy to copy data from the source to the destination, excluding specific file types.
Supports migrating a specific folder if specified.
 
.PARAMETER SourcePath
The full path to the source directory.
 
.PARAMETER DestinationPath
The full path to the destination directory.
 
.PARAMETER LogFilePath
The full path to the log file.
 
.PARAMETER Folder
The specific folder to migrate (e.g., "Desktop"). Optional. If not provided, the entire source directory is migrated.
 
.PARAMETER DryRun
Optional switch to test the migration process without copying files.
 
.EXAMPLE
Move-UserData -SourcePath "\\testing\w10-home\testu" -DestinationPath "\\testing\w10-home\test.user" -LogFilePath "C:\Logs\MigrationLog.log"
 
.EXAMPLE
Move-UserData -SourcePath "\\testing\w10-home\testu" -DestinationPath "\\testing\w10-home\test.user" -LogFilePath "C:\Logs\MigrationLog.log" -Folder "Desktop"
#>

function Move-UserData {
    param (
        [Parameter(Mandatory = $True)]
        [string]$SourcePath,

        [Parameter(Mandatory = $True)]
        [string]$DestinationPath,

        [Parameter(Mandatory = $True)]
        [string]$LogFilePath,

        [Parameter(Mandatory = $False)]
        [string]$Folder, # Optional parameter for a specific folder to migrate

        [switch]$DryRun # Optional switch to test without actually copying files
    )

    # Define file exclusions
    $excludedFiles = '*.pst', '*.ost', '*password*'

    # Validate source and destination paths
    if (-not (Test-Path -Path $SourcePath)) {
        Write-Log -Message "ERROR: Source path does not exist: $SourcePath" -LogFilePath $LogFilePath
        throw "Source path does not exist: $SourcePath"
    }
    if (-not (Test-Path -Path $DestinationPath)) {
        New-Item -ItemType Directory -Path $DestinationPath | Out-Null
        Write-Log -Message "INFO: Destination path created: $DestinationPath" -LogFilePath $LogFilePath
    }

    # Adjust source and destination paths if a specific folder is provided
    if ($PSBoundParameters.ContainsKey('Folder')) {
        $SourcePath = Join-Path -Path $SourcePath -ChildPath $Folder
        $DestinationPath = Join-Path -Path $DestinationPath -ChildPath $Folder

        # Validate the specific folder
        if (-not (Test-Path -Path $SourcePath)) {
            Write-Log -Message "ERROR: Specified folder does not exist in the source path: $SourcePath" -LogFilePath $LogFilePath
            throw "Specified folder does not exist: $SourcePath"
        }
    }

    # Log start of migration
    Write-Log -Message "INFO: Starting migration" -LogFilePath $LogFilePath
    Write-Log -Message "SOURCE: $SourcePath" -LogFilePath $LogFilePath
    Write-Log -Message "DESTINATION: $DestinationPath" -LogFilePath $LogFilePath
    Write-Log -Message "EXCLUDED FILE TYPES: $($excludedFiles -join ', ')" -LogFilePath $LogFilePath

    # Define Robocopy command with detailed logging
    $tempLogPath = [System.IO.Path]::GetTempFileName()
    $robocopyParams = @(
        '/E',            # Copy all subdirectories, including empty ones
        '/Z',            # Restartable mode
        '/COPY:DAT',     # Copy data, attributes, and timestamps
        '/R:2',          # Retry twice on failed files
        '/W:1',          # Wait 1 second between retries
        '/MT:16',        # Multithreaded copy
        '/V',            # Verbose logging (detailed file and folder info)
        '/XF',           # Exclude files
        $excludedFiles   # List of excluded files
    )

    $robocopyCommand = "robocopy.exe `"$SourcePath`" `"$DestinationPath`" *.* $($robocopyParams -join ' ') /LOG:`"$tempLogPath`""

    # Dry run mode
    if ($DryRun) {
        Write-Log -Message "DRY-RUN MODE ENABLED: No files will be copied." -LogFilePath $LogFilePath
        Write-Log -Message "DRY-RUN COMMAND: $robocopyCommand" -LogFilePath $LogFilePath
        return
    }

    # Execute Robocopy
    Write-Log -Message "INFO: Executing Robocopy..." -LogFilePath $LogFilePath
    Invoke-Expression -Command $robocopyCommand

    # Check for success
    if ($LASTEXITCODE -le 7) {
        Write-Log -Message "SUCCESS: Migration completed successfully." -LogFilePath $LogFilePath
    } else {
        Write-Log -Message "ERROR: Migration encountered issues. Exit code: $LASTEXITCODE" -LogFilePath $LogFilePath
    }

    # Parse detailed log from temporary Robocopy log
    Write-Log -Message "DETAILS: Files copied during the migration:" -LogFilePath $LogFilePath
    Write-Log -Message "-----------------------------------------------------" -LogFilePath $LogFilePath
    Get-Content -Path $tempLogPath | ForEach-Object {
        if ($_ -match "New File") {
            # Extract the file name only and clean the log format
            $fileName = ($_ -split "New File")[1].Trim() -replace '.*\\', '' # Extract only the file name
            $destinationFilePath = Join-Path -Path $DestinationPath -ChildPath $fileName
            Write-Log -Message "File '$fileName' copied to '$destinationFilePath'" -LogFilePath $LogFilePath
        }
    }
    Write-Log -Message "-----------------------------------------------------" -LogFilePath $LogFilePath

    # Clean up temporary log file
    Remove-Item -Path $tempLogPath -Force
}