From 9cb93c2386d4fe2202d81680341087d614234aab Mon Sep 17 00:00:00 2001 From: Tom Peltonen Date: Sat, 10 Jul 2021 14:21:47 +1000 Subject: [PATCH] Base script --- assets/logo.png | 0 ptrFiles.ps1 | 348 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 assets/logo.png create mode 100644 ptrFiles.ps1 diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..e69de29 diff --git a/ptrFiles.ps1 b/ptrFiles.ps1 new file mode 100644 index 0000000..d6f6249 --- /dev/null +++ b/ptrFiles.ps1 @@ -0,0 +1,348 @@ + + +param ( + [Parameter(Mandatory)][String] $Action, + [Parameter(Mandatory)][String] $Path, + [String] $RecipientKeyName, + [String] $TransferFileName, + [String] $ReconcileFileName = "##protect_transfer_reconcile##.csv", + [String] $SecretFileName = ".\transfer.key", + [switch] $ExcludeHash, + [switch] $Install7zip +) + +$default_dateLocal = Get-Date -Format "yyyyMMdd_HHmm" +$default_reconcileFile = "##protect_transfer_reconcile##.csv" +$default_secretEncrypted = ".\transfer.key" + + +function Write-Log { + param( + [String] $LogEntry + ) + + $date = Get-Date -f "yyyy-MM-dd" + + $logPath = Join-Path -Path ".\" -ChildPath "Logs" + $logName = "protect_transfer_reconcile_$date.log" + $sFullPath = Join-Path -Path $logPath -ChildPath $logName + + if (!(Test-Path -Path $logPath)) { + $null = New-Item -Path $logPath -ItemType Directory + } + + if (!(Test-Path -Path $sFullPath)) { + Write-Host "Log path: $sFullPath" + $null = New-Item -Path $sFullPath -ItemType File + } + $dateTime = Get-Date -f "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $sFullPath -Value "[$dateTime]. $LogEntry" + +} + +function Close-Log { + $dateTime = Get-Date -f "yyyy-MM-dd HH:mm:ss" + Write-Log "***********************************************************************************" + Write-Log "* End of processing: [$dateTime]" + Write-Log "***********************************************************************************" +} + +function New-RandomPassword { +param( + [int]$length = 20, + [String]$characters = "abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!()?}][{@#*+-", + [switch]$ConvertToSecureString +) + $password = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length } + $private:ofs="" + + if ($ConvertToSecureString.IsPresent) { + return ConvertTo-SecureString -String [String]$characters[$password] -AsPlainText -Force + } else { + return [String]$characters[$password] + } +} + + +# Reconcile +function Set-Reconcile +{ +Param( + [Parameter(Mandatory)][String] $ReconcileFile, + [Parameter(Mandatory)][String] $FolderName, + [switch]$Feedback = $false +) + + if ($reconcileFile -eq "") + { + $reconcileFile = $default_reconcileFile + } + + If (!(Test-Path -Path $folderName )) { + Write-Log "Folder '$folderName' does not exist" + Write-Host "Folder '$folderName' does not exist" -ForegroundColor Red + Close-Log + return + } + + Write-Log "Generating reconciliation file '$reconcileFile'" + Write-Host "Generating reconciliation file '$reconcileFile'" + + $totalFileCount = 0 + $totalFileSize = 0 + + Set-Content -Path $reconcileFile -Value '"FullName","LastWriteTime","Length","Hash","ParentFolder","Object"' + Get-Childitem $folderName -Recurse | Where-Object {!$_.PSIsContainer} | ForEach-Object { + $totalFilecount = $totalFileCount + 1 + $totalFileSize = $totalFileSize + $_.Length + if ($ExcludeHash) { + $sourceHash = "" + } else { + $sourceHash = (Get-FileHash -Path $_.FullName).Hash + } + $record = '"'+$_.FullName.Replace($folderName, "")+'","'+$_.LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss")+'",'+$_.Length+',"'+$sourceHash+'","'+ $_.Directory + '","' + $_.Name + '"' + Add-Content -Path $reconcileFile -Value $record + } + + if ($totalFileSize -ge 1000000000000) { + $totalRightLabel = "TB" + $totalFileXbytes = [math]::Round(($totalFileSize / 1000000000000), 2) + } else { + if ($totalFileSize -ge 1000000000) { + $totalRightLabel = "GB" + $totalFileXbytes = [math]::Round(($totalFileSize / 1000000000), 2) + } else { + if ($totalFileSize -ge 1000000) { + $totalRightLabel = "MB" + $totalFileXbytes = [math]::Round(($totalFileSize / 1000000), 2) + } else { + $totalRightLabel = "KB" + $totalFileXbytes = [math]::Round(($totalFileSize / 1000), 2) + } + } + } + + Write-Log "Total reconcile file count is $totalFileCount and size $totalFileXbytes $totalRightLabel" + if ($feedback) { + Write-Host "Total reconcile file count is $totalFileCount and size $totalFileXbytes $totalRightLabel" -ForegroundColor Green + } +} + + +function Invoke-Pack +{ +Param( + [String] $TransferFolder, + [String] $ToName, + [String] $CompressFile, + [String] $ReconcileFile, + [String] $SecretEncrypted +) + # Send + If (!(Test-Path -Path $transferFolder )) { + Write-Log "Folder '$transferFolder' does not exist" + Write-Host "Folder '$transferFolder' does not exist" -ForegroundColor Red + Close-Log + return + } + + + Write-Log "Packing files to compress file '$compressFile'" + Write-Log "Source folder is '$transferFolder'" + Write-Host "Packing files to compress file '$compressFile'" + + if ($reconcileFile -eq "") + { + $reconcileFile = $default_reconcileFile + } + + if ($secretEncrypted -eq "") + { + $secretEncrypted = $default_secretEncrypted + } + + # First step - create protected secret + $secret = New-RandomPassword -Length 80 + Protect-CmsMessage -To $toName -OutFile $secretEncrypted -Content $secret + + # Second step - compress the data files + Compress-7Zip -Path $transferFolder -ArchiveFileName $compressFile -Format SevenZip + + # Third step - create the reconcile file + Set-Reconcile -ReconcileFile $reconcileFile -FolderName $transferFolder + + # Fourth step - add reconcile file to ZIP and encrypt + Write-Log "Add reconcile file '$reconcileFile' to file '$compressFile'" + $fullReconcileName = (Get-Item $reconcileFile).FullName + $fullZipName = (Get-Item $compressFile).FullName + Compress-7Zip -Path $fullReconcileName -ArchiveFileName $fullZipName -Format SevenZip -Append -Password $secret -EncryptFilenames + Remove-Item $fullReconcileName + Write-Log "Package ready in file '$compressFile' from folder '$transferFolder'" + Write-Host "Package ready in file '$compressFile' from folder '$transferFolder'" -ForegroundColor Green +} + + +function Invoke-Unpack +{ +Param( + [String] $RestoreFolder, + [String] $ToName, + [String] $CompressFile, + [String] $SecretEncrypted +) + # Receive + If (!(Test-Path -Path $CompressFile )) { + Write-Log "Transfer/compress file '$CompressFile' does not exist" + Write-Host "Transfer/compress file '$CompressFile' does not exist" -ForegroundColor Red + Close-Log + return + } + + if ($secretEncrypted -eq "") + { + $secretEncrypted = $default_secretEncrypted + } + + # First step - decrypt password for uncompressing + Write-Log "Restoring files transferred to '$restoreFolder'" + Write-Log "Package/Compress file is '$compressFile'" + $secret = Unprotect-CmsMessage -To $toName -Path $secretEncrypted + + # Second step - uncompress the data files + Expand-7Zip -ArchiveFileName $compressFile -TargetPath $restoreFolder -Password $secret + Write-Log "Package unpacked from file '$compressFile' to folder '$restoreFolder'" + Write-Host "Package unpacked from file '$compressFile' to folder '$restoreFolder'" -ForegroundColor Green +} + + +# Reconcile +function Invoke-Reconcile +{ +Param( + [Parameter(Mandatory)][String] $reconcileFileName, + [Parameter(Mandatory)][String] $folderName +) + + Write-Log "Reconciling documents transferred" + Write-Host "Reconciling documents transferred" + If (!(Test-Path -Path $reconcileFileName )) { + Write-Log "Reconciliation file '$reconcileFileName' does not exist" + Write-Host "Reconciliation file '$reconcileFileName' does not exist" -ForegroundColor Red + Close-Log + return + } + If (!(Test-Path -Path $folderName )) { + Write-Log "Folder '$folderName' does not exist" + Write-Host "Folder '$folderName' does not exist" -ForegroundColor Red + Close-Log + return + } + Write-Log "Using reconciliation file '$reconcileFileName'" + + $totalFilecount = 0 + $errorCount = 0 + + # For each entry in the reconcile file + # find the file and compare hash + Import-Csv $reconcileFileName | ForEach-Object { + $totalFileCount = $totalFileCount +1 + $restoreFileName = $(Join-Path -Path $folderName -ChildPath $_.FullName) + If (Test-Path -Path $restoreFileName ) { + $targetHash= (Get-FileHash -Path $restoreFileName).Hash + if ($_.Hash -ne $targetHash) { + $errorCount = $errorCount + 1 + Write-Log "Hash mismatch for file '$restoreFileName'" + } + if ((Get-Item -Path $restoreFileName).LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss") -ne $_.LastWriteTime) { + $errorCount = $errorCount + 1 + Write-Log "Last write mismatch for file '$restoreFileName'" + } + + } else { + $errorCount = $errorCount + 1 + Write-Log "Non existant target file '$restoreFileName'" + } + } + + Write-Log "Total file count is $totalFileCount with $errorCount errors" + if ($errorCount -gt 0) { + Write-Host "Total file count is $totalFileCount with $errorCount errors" -ForegroundColor Red + } else { + Write-Host "Total file count is $totalFileCount with $errorCount errors" -ForegroundColor Green + } +} + +$dateTimeStart = Get-Date -f "yyyy-MM-dd HH:mm:ss" +Write-Log "***********************************************************************************" +Write-Log "* Start of processing: [$dateTimeStart]" +Write-Log "***********************************************************************************" + +if ($install7zip) { + Install-Module -Name 7Zip4Powershell -Scope CurrentUser +} + +$actioned = $false + +if ($action -eq "Pack") { + $actioned = $true + if ($RecipientKeyName -eq "") { + Write-Log "Recipient Key Name required for packing" + Write-Host "Recipient Key Name required for packing" -ForegroundColor Red + Close-Log + return + } + + if ($TransferFileName -eq "") { + $TransferFileName = ".\transfer_protect_$default_dateLocal.7z" + } + + Invoke-Pack -TransferFolder $path -ToName $recipientKeyName -CompressFile $transferFileName -ReconcileFile $reconcileFileName -SecretEncrypted $secretFileName + +} + + +if ($action -eq "Unpack") { + $actioned = $true + if ($RecipientKeyName -eq "") { + Write-Log "Recipient Key Name required for unpacking" + Write-Host "Recipient Key Name required for unpacking" -ForegroundColor Red + Close-Log + return + } + if ($TransferFileName -eq "") { + Write-Log "Transfer/Compress File Name required for unpacking" + Write-Host "Transfer/Compress File Name required for unpacking" -ForegroundColor Red + Close-Log + return + } + + Invoke-Unpack -RestoreFolder $path -ToName $recipientKeyName -CompressFile $transferFileName + +} + + +if ($action -eq "ReconcileFile") { + $actioned = $true + Set-Reconcile -ReconcileFile $reconcileFileName -FolderName $path -Feedback +} + + +if ($action -eq "Reconcile") { + $actioned = $true + $localReconcileFile = Join-Path -Path $path -ChildPath $reconcileFileName + Invoke-Reconcile -ReconcileFile $localReconcileFile -FolderName $path +} + + +if (!($actioned)) +{ + Write-Log "Unknown action '$action'. No processing performed" + Write-Host "Unknown action '$action'. No processing performed" -ForegroundColor Red + Write-Host "Recognised actions: " + Write-Host " Pack : Pack folder contents into secure 7Zip file" + Write-Host " Unpack : Unpack folder contents from secure 7Zip file" + Write-Host " Reconcile : Reconcile files in unpack folder with list of packed files" + Write-Host " ReconcileFile : Generate a reconcile file without packing" +} + +Close-Log