Skip to content

Instantly share code, notes, and snippets.

@ll4mat
Created May 14, 2024 07:43
Show Gist options
  • Save ll4mat/a5c94bb2bca4521b1cba2c550c698481 to your computer and use it in GitHub Desktop.
Save ll4mat/a5c94bb2bca4521b1cba2c550c698481 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Automates the copying (copy-deleting) of new files from a network share to a local directory and logs the actions taken.
.DESCRIPTION
This script monitors specified folders on a network share for new files using a FileSystemWatcher. When a new file is detected, it is copied to a local destination directory. The script logs all operations and can be stopped by the presence of an exit condition file. It also includes a mode that allows for copying without removing the original files.
.PARAMETER CopyOnly
When enabled, the script will copy files as usual but will not delete the source files after copying. This mode is useful for verifying the copy process without modifying the source files.
.PARAMETER credentialPath
The path to the XML file containing the credentials for the network share.
.PARAMETER DestDir
The destination directory where files should be copied to.
.PARAMETER SrcShare
The (UNC) path to the source network share.
.PARAMETER logFile
The path to the log file where actions should be logged.
.PARAMETER netDrive
The name of the temporary PSDrive that maps to the network share.
.PARAMETER exitConditionFile
The path to a file that, when deleted, will cause the script to stop running.
.PARAMETER maxConcurrentJobs
The maximum number of concurrent jobs that can run.
.PARAMETER subFoldersToProcess
An array of subfolder names to process.
.EXAMPLE
.\CopyScript.ps1 -CopyOnly -credentialPath "C:\Credentials.xml" -DestDir "D:\Destination" -SrcShare "\\Server\Share" -logFile "D:\CopyScript.log" -netDrive "NetDrive" -exitConditionFile "D:\StopCopy.lock" -maxConcurrentJobs 5 -subFoldersToProcess @('Folder1', 'Folder2')
This example runs the script with the specified parameters, copying files without deleting the originals from the source share.
#>
param(
[switch]$CopyOnly,
[string]$credentialPath = "C:\Path\To\Credentials.xml",
[string]$DestDir = "D:\Data\DestinationFolder",
[string]$SrcShare = "\\Server\Share\Subfolder1\Subfolder2",
[string]$logFile = "D:\Logs\CopyScript.log",
[string]$netDrive = "Temp_NetworkDrive1",
[string]$exitConditionFile = "D:\Data\StopCopy.lock",
[int]$maxConcurrentJobs = 5,
[string[]]$subFoldersToProcess = @('FOO', 'BAR', 'BAZ', 'QUX', 'THUD', 'WALDO', 'CORGE')
)
# Determine the list separator for the current culture
$listSeparator = (Get-Culture).TextInfo.ListSeparator
# Write-Log function
function Write-Log {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$message
)
Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')$listSeparator$message"
}
# Initialize-Log function
function Initialize-Log {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$logFilePath
)
if (-Not (Test-Path -Path $logFilePath)) {
New-Item -Path $logFilePath -ItemType File
Write-Log -message "Log file created at $logFilePath on $(Get-Date -Format 'yyyy-MM-dd')."
} else {
Write-Host "Log file already exists at $logFilePath"
}
}
# Cleanup completed and failed jobs function
function Remove-JobCompletedOrFailed {
[CmdletBinding()]
param()
Get-Job | Where-Object { $_.State -eq 'Completed' -or $_.State -eq 'Failed' } | ForEach-Object {
$job = $_
if ($job.State -eq 'Failed') {
Write-Log -message "Job $($job.Id) failed with error: $($job.ChildJobs[0].Error[0])"
$script:exitCode = 1
$script:stopScript = $true
}
Remove-Job -Job $job
}
}
# Import credentials for mounting the source-share
$cred = Import-Clixml -Path $credentialPath
# Initialize log file
Initialize-Log -logFilePath $logFile
# Map network share to a temporary PSDrive
New-PSDrive -Name $netDrive -PSProvider FileSystem -Root $SrcShare -Credential $cred
# Create the exit condition file
New-Item -Path $exitConditionFile -ItemType File
# Initialize FileSystemWatcher
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "${netDrive}:\"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
# Event handler
$handler = {
param($source, $e)
$subFolderName = [System.IO.Path]::GetDirectoryName($e.Name)
if ($subFolderName -in $subFoldersToProcess) {
$newFilePath = $e.FullPath
$destinationPath = Join-Path -Path $DestDir -ChildPath $e.Name
while ((Get-Job -State Running).Count -ge $maxConcurrentJobs) {
Start-Sleep -Seconds 1
Remove-JobCompletedOrFailed
}
Start-Job -ScriptBlock {
param($sourcePath, $destPath, $logPath, $CopyOnly)
function Write-Log {
Param ([string]$message)
Add-Content -Path $logPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): $message"
}
try {
if (-Not (Test-Path -Path $destPath)) {
Copy-Item -Path $sourcePath -Destination $destPath
Write-Log "File $sourcePath was copied to $destPath."
if (-not $CopyOnly) {
Remove-Item -Path $sourcePath
Write-Log "File $sourcePath was deleted from Network-Share."
} else {
Write-Log "CopyOnly Mode is ON: File $sourcePath was not deleted from Network-Share."
}
}
} catch {
Write-Log "An error occurred: $_"
Write-Log "The script will be terminated as a precaution."
$script:exitCode = 1
Throw
}
} -ArgumentList $newFilePath, $destinationPath, $logFile, $CopyOnly
}
}
# Register event handler with named parameter
Register-ObjectEvent -InputObject $watcher -EventName Created -Action $handler
# Main loop
while (Test-Path -Path $exitConditionFile -and -not $script:stopScript) {
Start-Sleep -Seconds 10
Remove-JobCompletedOrFailed
}
# Cleanup and release resources
try {
if ($watcher) {
$watcher.Dispose()
Write-Log "The FileSystemWatcher was disposed successfully."
}
} catch {
Write-Log "An error occurred while disposing the FileSystemWatcher: $_"
$exitCode = 1
}
try {
Remove-PSDrive -Name $netDrive -ErrorAction Stop
Write-Log "Network drive $netDrive was removed successfully."
} catch {
Write-Log "An error occurred while removing the network drive '$netDrive': $_"
$exitCode = 1
}
# Exit with the determined exit code
Exit $exitCode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment