EnhancedFileManagerAO.psm1
#Region '.\Private\Check-DiskSpace.ps1' -1 function Check-DiskSpace { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string]$Path, [Parameter(Mandatory = $true)] [int]$RequiredSpaceGB ) begin { Write-EnhancedLog -Message "Starting Check-DiskSpace function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } process { try { $drive = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -eq ([System.IO.Path]::GetPathRoot($Path)) } if ($drive -and ($drive.Free -lt ($RequiredSpaceGB * 1GB))) { Write-EnhancedLog -Message "Not enough disk space on drive $($drive.Root). Required: $RequiredSpaceGB GB, Available: $([math]::round($drive.Free / 1GB, 2)) GB." -Level "ERROR" throw "Not enough disk space on drive $($drive.Root)." } else { Write-EnhancedLog -Message "Sufficient disk space available on drive $($drive.Root)." -Level "INFO" } } catch { Write-EnhancedLog -Message "An error occurred during disk space check: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } end { Write-EnhancedLog -Message "Check-DiskSpace function execution completed." -Level "Notice" } } #EndRegion '.\Private\Check-DiskSpace.ps1' 35 #Region '.\Private\Handle-RobocopyExitCode.ps1' -1 function Handle-RobocopyExitCode { <# .SYNOPSIS Handles the exit code from a Robocopy operation. .DESCRIPTION The Handle-RobocopyExitCode function interprets the exit code returned by a Robocopy operation and logs an appropriate message. The exit codes provide information about the success or failure of the operation and any additional conditions encountered. .PARAMETER ExitCode The exit code returned by Robocopy. .EXAMPLE Handle-RobocopyExitCode -ExitCode 0 Logs a message indicating no files were copied, no files were mismatched, and no failures were encountered. .EXAMPLE Handle-RobocopyExitCode -ExitCode 1 Logs a message indicating all files were copied successfully. .NOTES This function is typically used internally after executing a Robocopy command to handle and log the exit status. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [int]$ExitCode ) begin { Write-EnhancedLog -Message "Starting Handle-RobocopyExitCode function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } process { try { switch ($ExitCode) { 0 { Write-EnhancedLog -Message "No files were copied. No files were mismatched. No failures were encountered." -Level "INFO" } 1 { Write-EnhancedLog -Message "All files were copied successfully." -Level "INFO" } 2 { Write-EnhancedLog -Message "There are some additional files in the destination directory that are not present in the source directory. No files were copied." -Level "INFO" } 3 { Write-EnhancedLog -Message "Some files were copied. Additional files were present. No failure was encountered." -Level "INFO" } 4 { Write-EnhancedLog -Message "Some files were mismatched. No files were copied." -Level "INFO" } 5 { Write-EnhancedLog -Message "Some files were copied. Some files were mismatched. No failure was encountered." -Level "INFO" } 6 { Write-EnhancedLog -Message "Additional files and mismatched files exist. No files were copied." -Level "INFO" } 7 { Write-EnhancedLog -Message "Files were copied, a file mismatch was present, and additional files were present." -Level "INFO" } 8 { Write-EnhancedLog -Message "Several files did not copy." -Level "ERROR" } default { Write-EnhancedLog -Message "Robocopy failed with exit code $ExitCode" -Level "ERROR" } } } catch { Write-EnhancedLog -Message "An error occurred while handling the Robocopy exit code: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } end { Write-EnhancedLog -Message "Handle-RobocopyExitCode function execution completed." -Level "Notice" } } #EndRegion '.\Private\Handle-RobocopyExitCode.ps1' 61 #Region '.\Private\Test-Directory.ps1' -1 function Test-Directory { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string]$Path ) begin { Write-EnhancedLog -Message "Starting Test-Directory function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } process { try { if (-Not (Test-Path -Path $Path -PathType Container)) { Write-EnhancedLog -Message "The path '$Path' is not a valid directory." -Level "ERROR" throw "The path '$Path' is not a valid directory." } else { Write-EnhancedLog -Message "The path '$Path' is a valid directory." -Level "INFO" } } catch { Write-EnhancedLog -Message "An error occurred during directory validation: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } end { Write-EnhancedLog -Message "Test-Directory function execution completed." -Level "Notice" } } #EndRegion '.\Private\Test-Directory.ps1' 32 #Region '.\Private\Test-Robocopy.ps1' -1 function Test-Robocopy { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$RobocopyPath = "C:\Windows\System32\Robocopy.exe" ) begin { Write-EnhancedLog -Message "Starting Test-Robocopy function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } process { try { if (-Not (Test-Path -Path $RobocopyPath)) { Write-EnhancedLog -Message "Robocopy.exe is not available at the specified path: $RobocopyPath" -Level "ERROR" throw "Robocopy.exe is not available at the specified path: $RobocopyPath" } else { Write-EnhancedLog -Message "Robocopy.exe is available at the specified path: $RobocopyPath" -Level "INFO" } } catch { Write-EnhancedLog -Message "An error occurred during Robocopy availability check: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } end { Write-EnhancedLog -Message "Test-Robocopy function execution completed." -Level "Notice" } } #EndRegion '.\Private\Test-Robocopy.ps1' 35 #Region '.\Public\Copy-FilesWithRobocopy.ps1' -1 function Copy-FilesWithRobocopy { <# .SYNOPSIS Copies files from a source directory to a destination directory using Robocopy and verifies the operation. .DESCRIPTION The Copy-FilesWithRobocopy function copies files from a source directory to a destination directory based on a specified file pattern using Robocopy. It validates the source and destination directories, checks disk space, logs the Robocopy process, and verifies the copy operation. It also handles locked files by finding and killing the locking processes. .PARAMETER Source The source directory from which files will be copied. .PARAMETER Destination The destination directory to which files will be copied. .PARAMETER FilePattern The file pattern to match files that should be copied. If not provided, defaults to '*'. .PARAMETER RetryCount The number of retries if a copy fails. Default is 2. .PARAMETER WaitTime The wait time between retries in seconds. Default is 5. .PARAMETER RequiredSpaceGB The required free space in gigabytes at the destination. Default is 10 GB. .PARAMETER Exclude The directories or files to exclude from the copy operation. .EXAMPLE Copy-FilesWithRobocopy -Source "C:\Source" -Destination "C:\Destination" -FilePattern "*.txt" Copies all .txt files from C:\Source to C:\Destination. .EXAMPLE "*.txt", "*.log" | Copy-FilesWithRobocopy -Source "C:\Source" -Destination "C:\Destination" Copies all .txt and .log files from C:\Source to C:\Destination using pipeline input for the file patterns. .EXAMPLE Copy-FilesWithRobocopy -Source "C:\Source" -Destination "C:\Destination" -Exclude ".git" Copies files from C:\Source to C:\Destination excluding the .git folder. .NOTES This function relies on the following private functions: - Check-DiskSpace.ps1 - Handle-RobocopyExitCode.ps1 - Test-Directory.ps1 - Test-Robocopy.ps1 - Verify-CopyOperation.ps1 #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Source, [Parameter(Mandatory = $true)] [string]$Destination, [Parameter(Mandatory = $false, ValueFromPipeline = $true)] [string]$FilePattern = '*', # Default to '*' if not provided [Parameter(Mandatory = $false)] [int]$RetryCount = 2, [Parameter(Mandatory = $false)] [int]$WaitTime = 5, [Parameter(Mandatory = $false)] [int]$RequiredSpaceGB = 10, # Example value for required space [Parameter(Mandatory = $false)] [string[]]$Exclude ) begin { Write-EnhancedLog -Message "Starting Copy-FilesWithRobocopy function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters # Validate Robocopy, source, and destination directories try { Test-Robocopy Test-Directory -Path $Source Write-EnhancedLog -Message "Validated source directory: $Source" -Level "INFO" Test-Directory -Path $Destination Write-EnhancedLog -Message "Validated destination directory: $Destination" -Level "INFO" } catch { Write-EnhancedLog -Message "Critical error during validation of source or destination directories or Robocopy validation." -Level "CRITICAL" Handle-Error -ErrorRecord $_ throw $_ } # Check disk space try { Check-DiskSpace -Path $Destination -RequiredSpaceGB $RequiredSpaceGB } catch { Write-EnhancedLog -Message "Critical error during disk space validation." -Level "CRITICAL" Handle-Error -ErrorRecord $_ throw $_ } # Prepare Robocopy log file path $timestamp = Get-Date -Format "yyyyMMddHHmmss" $logFilePath = "$env:TEMP\RobocopyLog_$timestamp.log" Write-EnhancedLog -Message "Robocopy log file will be saved to: $logFilePath" -Level "INFO" } process { try { $robocopyPath = "C:\Windows\System32\Robocopy.exe" $robocopyArgs = @( "`"$Source`"", "`"$Destination`"", $FilePattern, "/E", "/R:$RetryCount", "/W:$WaitTime", "/LOG:`"$logFilePath`"" ) # Add exclude arguments if provided if ($Exclude) { $excludeDirs = $Exclude | ForEach-Object { "/XD `"$($_)`"" } $excludeFiles = $Exclude | ForEach-Object { "/XF `"$($_)`"" } $robocopyArgs = $robocopyArgs + $excludeDirs + $excludeFiles # Log what is being excluded foreach ($item in $Exclude) { Write-EnhancedLog -Message "Excluding: $item" -Level "INFO" } } Write-EnhancedLog -Message "Starting Robocopy process with arguments: $robocopyArgs" -Level "INFO" # Splatting Start-Process parameters $startProcessParams = @{ FilePath = $robocopyPath ArgumentList = $robocopyArgs NoNewWindow = $true Wait = $true PassThru = $true } $process = Start-Process @startProcessParams if ($process.ExitCode -ne 0) { Write-EnhancedLog -Message "Robocopy process failed with exit code: $($process.ExitCode)" -Level "CRITICAL" } Handle-RobocopyExitCode -ExitCode $process.ExitCode } catch { Write-EnhancedLog -Message "An error occurred while copying files with Robocopy: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ # Check if the error is due to a file being used by another process if ($_.Exception -match "because it is being used by another process") { Write-EnhancedLog -Message "Attempting to find and kill the process locking the file." -Level "WARNING" try { # Find the process locking the file $lockedFile = $_.Exception.Message -match "'(.+?)'" | Out-Null $lockedFile = $matches[1] # Kill the processes locking the file Kill-LockingProcesses -LockedFile $lockedFile # Retry the Robocopy operation Write-EnhancedLog -Message "Retrying Robocopy operation after killing the locking process." -Level "INFO" $process = Start-Process @startProcessParams if ($process.ExitCode -ne 0) { Write-EnhancedLog -Message "Robocopy process failed again with exit code: $($process.ExitCode)" -Level "CRITICAL" } Handle-RobocopyExitCode -ExitCode $process.ExitCode Write-EnhancedLog -Message "Copy operation retried and succeeded." -Level "INFO" } catch { Write-EnhancedLog -Message "Failed to find or kill the process locking the file: $lockedFile" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } else { throw $_ } } } end { Write-EnhancedLog -Message "Verifying copied files..." -Level "Notice" # Call Verify-CopyOperation to ensure the files were copied correctly Verify-CopyOperation -SourcePath $Source -DestinationPath $Destination Write-EnhancedLog -Message "Copy-FilesWithRobocopy function execution completed." -Level "Notice" } } # # Example usage # $sourcePath = "C:\Source" # $destinationPath = "C:\Destination" # Copy-FilesWithRobocopy -Source $sourcePath -Destination $destinationPath #EndRegion '.\Public\Copy-FilesWithRobocopy.ps1' 201 #Region '.\Public\Copy-FileToPublicAndTemp.ps1' -1 function Copy-FileToPublicAndTemp { <# .SYNOPSIS Copies a specified file to the public desktop and C:\temp, making it available to all users and in the temp directory. .DESCRIPTION This function copies a specified file to the public desktop and C:\temp, ensuring it is available to all users and also in the temp directory. Enhanced logging is used for feedback and error handling. .PARAMETER SourceFilePath The path of the file to be copied. .EXAMPLE Copy-FileToPublicAndTemp -SourceFilePath "C:\Path\To\fcremove.exe" This example copies the file "fcremove.exe" to the public desktop and C:\temp, making it available to all users and in the temp directory. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$SourceFilePath ) begin { Write-EnhancedLog -Message 'Starting Copy-FileToPublicAndTemp function' -Level 'INFO' Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } process { try { if (-not (Test-Path -Path $SourceFilePath)) { Write-EnhancedLog -Message "Source file not found: $SourceFilePath" -Level "ERROR" throw "Source file not found: $SourceFilePath" } # Define the destination paths $publicDesktopPath = 'C:\Users\Public\Desktop' $tempPath = 'C:\temp' # Ensure the public desktop directory exists $publicDesktopParams = @{ Path = $publicDesktopPath ItemType = 'Directory' Force = $true ErrorAction = 'Stop' } if (-not (Test-Path -Path $publicDesktopPath)) { Write-EnhancedLog -Message "Public desktop path not found. Creating directory." -Level "INFO" New-Item @publicDesktopParams | Out-Null } # Ensure the temp directory exists $tempParams = @{ Path = $tempPath ItemType = 'Directory' Force = $true ErrorAction = 'Stop' } if (-not (Test-Path -Path $tempPath)) { Write-EnhancedLog -Message "Temp path not found. Creating directory." -Level "INFO" New-Item @tempParams | Out-Null } # Copy the file to the public desktop $destinationFilePathPublic = Join-Path -Path $publicDesktopPath -ChildPath (Split-Path -Leaf $SourceFilePath) $copyParamsPublic = @{ Path = $SourceFilePath Destination = $destinationFilePathPublic Force = $true ErrorAction = 'Stop' } Write-EnhancedLog -Message "Copying file to: $destinationFilePathPublic" -Level "INFO" Copy-Item @copyParamsPublic Write-EnhancedLog -Message "File copied to: $destinationFilePathPublic" -Level "INFO" # Copy the file to the temp directory $destinationFilePathTemp = Join-Path -Path $tempPath -ChildPath (Split-Path -Leaf $SourceFilePath) $copyParamsTemp = @{ Path = $SourceFilePath Destination = $destinationFilePathTemp Force = $true ErrorAction = 'Stop' } Write-EnhancedLog -Message "Copying file to: $destinationFilePathTemp" -Level "INFO" Copy-Item @copyParamsTemp Write-EnhancedLog -Message "File copied to: $destinationFilePathTemp" -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while copying the file: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ } } end { Write-EnhancedLog -Message 'Copy-FileToPublicAndTemp function completed' -Level 'INFO' } } #EndRegion '.\Public\Copy-FileToPublicAndTemp.ps1' 98 #Region '.\Public\Find-LockingProcesses.ps1' -1 function Find-LockingProcesses { <# .SYNOPSIS Finds processes that are locking a specified file. .DESCRIPTION The Find-LockingProcesses function identifies processes that are locking a specified file by checking the modules loaded by each process. .PARAMETER LockedFile The path to the locked file. .EXAMPLE $lockingProcesses = Find-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt" Finds processes locking the specified file and returns the processes. .NOTES This function relies on the Get-Process cmdlet and its ability to enumerate loaded modules. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$LockedFile ) Begin { Write-EnhancedLog -Message "Starting Find-LockingProcesses function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { Write-EnhancedLog -Message "Finding processes locking file: $LockedFile" -Level "INFO" $lockingProcesses = Get-Process | Where-Object { $_.Modules | Where-Object { $_.FileName -eq $LockedFile } } if ($lockingProcesses) { foreach ($process in $lockingProcesses) { Write-EnhancedLog -Message "Process locking the file: $($process.ProcessName) (ID: $($process.Id))" -Level "INFO" } } else { Write-EnhancedLog -Message "No processes found locking the file: $LockedFile" -Level "INFO" } return $lockingProcesses } catch { Write-EnhancedLog -Message "An error occurred while finding locking processes: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Find-LockingProcesses function" -Level "Notice" } } # Example usage # $lockingProcesses = Find-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt" #EndRegion '.\Public\Find-LockingProcesses.ps1' 57 #Region '.\Public\Kill-LockingProcesses.ps1' -1 function Kill-LockingProcesses { <# .SYNOPSIS Kills processes that are locking a specified file. .DESCRIPTION The Kill-LockingProcesses function finds processes that are locking a specified file and terminates them forcefully. .PARAMETER LockedFile The path to the locked file. .EXAMPLE Kill-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt" Finds and kills processes locking the specified file. .NOTES This function relies on the Find-LockingProcesses function to identify locking processes. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$LockedFile ) Begin { Write-EnhancedLog -Message "Starting Kill-LockingProcesses function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { Write-EnhancedLog -Message "Finding processes locking file: $LockedFile" -Level "INFO" $lockingProcesses = Find-LockingProcesses -LockedFile $LockedFile if ($lockingProcesses) { foreach ($process in $lockingProcesses) { Write-EnhancedLog -Message "Killing process $($process.ProcessName) (ID: $($process.Id)) locking the file $LockedFile" -Level "INFO" Stop-Process -Id $process.Id -Force -Confirm:$false } } else { Write-EnhancedLog -Message "No processes found locking the file: $LockedFile" -Level "INFO" } } catch { Write-EnhancedLog -Message "An error occurred while killing locking processes: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Kill-LockingProcesses function" -Level "Notice" } } # Example usage # Kill-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt" #EndRegion '.\Public\Kill-LockingProcesses.ps1' 58 #Region '.\Public\Validate-FileExists.ps1' -1 function Validate-FileExists { param ( [Parameter(Mandatory = $true)] [string]$FilePath ) if (-Not (Test-Path -Path $FilePath)) { Write-EnhancedLog -Message "File '$FilePath' does not exist." -Level "ERROR" throw "File '$FilePath' does not exist." } } #EndRegion '.\Public\Validate-FileExists.ps1' 14 #Region '.\Public\Verify-CopyOperation.ps1' -1 function Verify-CopyOperation { <# .SYNOPSIS Verifies that files have been correctly copied between a source and destination directory. .DESCRIPTION The Verify-CopyOperation function compares the contents of a source directory with a destination directory to ensure that all files and directories have been successfully copied. It reports missing files, extra files, and provides detailed information about any discrepancies found during the verification process. .PARAMETER SourcePath The path to the source directory whose contents are being copied and need verification. .PARAMETER DestinationPath The path to the destination directory where the files from the source have been copied. .EXAMPLE Verify-CopyOperation -SourcePath "C:\Source" -DestinationPath "C:\Destination" Verifies the copied contents between C:\Source and C:\Destination, checking for any discrepancies such as missing or extra files. .OUTPUTS Custom objects detailing missing, extra, or mismatched files between the source and destination. .NOTES The function uses recursion to verify subdirectories and outputs the results to the console. Any discrepancies between the source and destination directories are logged for analysis. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = "Provide the source directory path.")] [string]$SourcePath, [Parameter(Mandatory = $true, HelpMessage = "Provide the destination directory path.")] [string]$DestinationPath ) begin { Write-EnhancedLog -Message "Verifying copy operation..." -Level "Notice" Log-Params -Params @{ SourcePath = $SourcePath DestinationPath = $DestinationPath } $sourceItems = Get-ChildItem -Path $SourcePath -Recurse -File $destinationItems = Get-ChildItem -Path $DestinationPath -Recurse -File # Use a generic list for better performance compared to using an array with += $verificationResults = [System.Collections.Generic.List[PSCustomObject]]::new() } process { try { foreach ($item in $sourceItems) { $relativePath = $item.FullName.Substring($SourcePath.Length) $correspondingPath = Join-Path -Path $DestinationPath -ChildPath $relativePath if (-not (Test-Path -Path $correspondingPath)) { $verificationResults.Add([PSCustomObject]@{ Status = "Missing" SourcePath = $item.FullName ExpectedPath = $correspondingPath FileSize = $item.Length LastModified = $item.LastWriteTime }) } else { # Compare file sizes and timestamps $destItem = Get-Item -Path $correspondingPath if ($item.Length -ne $destItem.Length -or $item.LastWriteTime -ne $destItem.LastWriteTime) { $verificationResults.Add([PSCustomObject]@{ Status = "Mismatch" SourcePath = $item.FullName ExpectedPath = $correspondingPath SourceSize = $item.Length DestinationSize = $destItem.Length SourceModified = $item.LastWriteTime DestinationModified = $destItem.LastWriteTime }) } } } foreach ($item in $destinationItems) { $relativePath = $item.FullName.Substring($DestinationPath.Length) $correspondingPath = Join-Path -Path $SourcePath -ChildPath $relativePath if (-not (Test-Path -Path $correspondingPath)) { $verificationResults.Add([PSCustomObject]@{ Status = "Extra" ActualPath = $item.FullName FileSize = $item.Length LastModified = $item.LastWriteTime }) } } } catch { Write-EnhancedLog -Message "Error during verification process: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } end { if ($verificationResults.Count -gt 0) { Write-EnhancedLog -Message "Discrepancies found. See detailed log." -Level "WARNING" $verificationResults | Format-Table -AutoSize | Out-String | ForEach-Object { Write-EnhancedLog -Message $_ -Level "INFO" } # Uncomment when troubleshooting $verificationResults | Out-GridView } else { Write-EnhancedLog -Message "All items verified successfully. No discrepancies found." -Level "Notice" } Write-EnhancedLog -Message ("Total items in source: $SourcePath " + $sourceItems.Count) -Level "INFO" Write-EnhancedLog -Message ("Total items in destination: $DestinationPath " + $destinationItems.Count) -Level "INFO" # Return the verification results for further processing return $verificationResults } } #EndRegion '.\Public\Verify-CopyOperation.ps1' 124 |