2021-07-10 04:27:55 +00:00
<#
. Synopsis
Allows the secure transfer and reconciliation of a large number of files
2021-07-10 04:28:54 +00:00
PTRfile : Protect , Transfer , Reconcile files
2021-07-10 04:27:55 +00:00
. Description
Packages source folder contents into a 7ZIP file , adding a reconciliation
file to the 7ZIP file and then encrypting the contents . Send
* this script
* the 7ZIP package file
* plus optional SecretFilename ( if using RecipientKeyName )
to the target or recipient .
The source folder is not altered and only read rights are required . A log
file is written at exceution to record activity .
The SecretFileName can be sent via email , while the 7ZIP can go different routes
2021-07-10 04:28:54 +00:00
due to possible size such as :
* Cloud storage provider
2021-07-10 04:27:55 +00:00
* HTTPS web file upload
* SFTP transfer
* USB stick
At the target , unpack the contents to a folder and reconcile the results . You
will need write access on the target storage . A log file is written at exceution
to record activity .
Your bulk file transfer is encrypted in transit . Note that if you use the
SecretKey method the ecnrypted contents will only be as secure as the strength
of your secret .
2021-07-10 04:28:54 +00:00
You can use storage providers such as Dropbox , AWS S3 , Google Drive , OneDrive or BackBlaze
and your documents have additonal protection .
2021-07-10 04:27:55 +00:00
A log file is produced on execution . Repeated executions on the same day
will add text content to the same log file . The default log name takes the form :
2021-07-10 04:28:54 +00:00
" ptr_files_yyyy-MM-dd.log "
2021-07-10 04:27:55 +00:00
You will need to have installed the 7Zip4Powershell PowerShell cmdlet
before using the pack or unpack actions . You can install the cmdlet
by executing
2021-07-10 04:28:54 +00:00
. \ ptrFiles . ps1 -Action install -Path " .\ "
2021-07-10 04:27:55 +00:00
Author : Tom Peltonen
. Parameter Action
Action to perform , which can be :
2021-07-11 09:05:44 +00:00
- Install : Install 7Zip4PowerShell
- Pack : Archive the contents of a folder ( s )
- Unpack : Unpack the archive , but no reconfile is performed
- Reconcile : Reconcile the contents in the restored folder
- ReconcileFile : Generate reconfile file . The pack process does this .
- ArchiveInformation : Fetch archive information
2021-07-10 04:27:55 +00:00
. Parameter Path
The path to the files and folders to pack or the path to the unpack location .
The path can include a trailing * as a wildcard to only include a subset of
directories .
When using the trailing * for names , the filter ing is only applied to immediate
folder names under the parent folder . The filter does not cascade to lower folders .
2021-07-11 09:05:44 +00:00
The Path can also be a file containing a list of paths , one per line . To use a
list file , prefix the Path value with a " @ " and name the file . Do not use a folder
for @ defined path .
A file ( @ prefix ) containing a list of paths cannot contain generic path names , that
is paths with trailing wildcard of " * "
2021-07-10 04:27:55 +00:00
. Parameter RecipientKeyName
The recipient of the package which is used to find the appropriate
certificate for encrypting with the public key . Either the RecipientKeyName
or the SecretKey is required for packing or unpacking the 7ZIP file .
Using the RecipientKeyName is the most secure transfer option as a
asymmetric cryptographic key is used that can only be decrypted by the
holder of the private key .
If you are using the RecipientKeyName , then the 7ZIP file contents can only
be unzipped by the holder of the private key and the SecretFileName file .
If you don ' t have the private , which you should not unless you are sending
to yourself , then you cannot unpack the 7ZIP file .
. Parameter SecretKey
A tradiitional secret to encrypt or decrypt the 7ZIP package . Either the RecipientKeyName
or the SecretKey is required for packing or unpacking the 7ZIP file . This method
uses a symmetric cryptographic key exchange which is less secure then the
RecipientKeyName approach .
2021-07-11 09:05:44 +00:00
Note : Currently the script doe snot user Secure Strings
2021-07-11 05:21:50 +00:00
. Parameter ArchiveFileName
2021-07-10 04:27:55 +00:00
The location and name of the 7ZIP file . If not supplied a default 7ZIP file name
will be generated in the current directory .
The default name will take the form " .\transfer_protect_yyyyMMdd_hhmm.7z "
. Parameter RootFolderName
The root folder , which should be used if using wildcard ( * ) for the
path . A guess will be made as to value if not supplied , which will
2021-07-10 04:28:54 +00:00
work in many circumstances .
. Parameter FileFilter
A filter on file names . This does not filter directories .
An example to only include JPEG file is " *.jpg " . You can also
filter on picture file names starting with " IMG*.jpg "
2021-07-10 04:27:55 +00:00
. Parameter ReconcileFileName
The name of the reconfile file name to generate during pack or use
during unpack . This is a file name without path . If no value is
supplied , then a default name is generated .
The reconcile file is included into the root of the 7ZIP file .
Once a reconcile is executed , you can delete this file from the
restored location .
2021-07-10 04:28:54 +00:00
The default name is " ##protect_transfer_reconcile_files##.csv "
2021-07-10 04:27:55 +00:00
. Parameter SecretFileName
The secret file name is used with RecipientKeyName to secure the
internally generated password for the 7ZIP file . When unpacking the
7ZIP file you will need access to this file if RecipientKeyName
was used . If not supplied a default name is used . This file is
encrypted with RecipientKeyName .
The default name is " .\transfer.key "
. Parameter ExcludeHash
Exclude the file hash from the reconcile file . As producing a file
hash takes compute cycles during pack , you can select to bypass this
generation to speed up the packaging . Excluding the hash does reduce
the function ality of the reconciliation at unpack .
. Parameter LogPath
The log folder where log files are written . If the folder does not
exist then it is created . You need write access rights to this location .
. Notes
This script has been written to use the 7ZIP function as it is open source
and provides a secure encryption mechanism , plus portability on Windows ,
Linux and MacOS .
It is also beneficial that 7ZIP has efficient compression algorithms .
Compressing and packing a large data set can take significant time and also
require storage space . The script does not check if you have sufficient
free storage to package the source contents into a single 7ZIP file . It is your
responsibility to ensure sufficient storage space exists .
2021-07-10 04:28:54 +00:00
If you need to copy files from one directory to another accessible directory from
your Windows desktop , you might consider using ROBOCOPY . If the target directory
is not accessible and you want to reconcile , then this tool is appropriate .
2021-07-10 04:27:55 +00:00
. Example
# Pack and encrypt all files in folder ".\transferpack\" using a private-public key
# A file named ".\transfer.key" is also generated alongside the 7ZIP file
2021-07-10 04:28:54 +00:00
. \ ptrFiles . ps1 -Action pack -Path " .\transferpack\ " -RecipientKeyName data @mycompany
2021-07-10 04:27:55 +00:00
. Example
# Unpack all files in 7ZIP file "transfer_protect_yyyMMdd_hhmm.7z" to folder ".\targetdir" using a private-public key
# You will need the file named ".\transfer.key" to unpack the encrypted 7ZIP file
2021-07-11 05:21:50 +00:00
. \ ptrFiles . ps1 -Action unpack -ArchiveFileName " transfer_protect_yyyMMdd_hhmm.7z " -Path " .\targetdir " -RecipientKeyName data @mycompany
2021-07-10 04:27:55 +00:00
. Example
# Reconcile files in folder ".\targetdir"
2021-07-10 04:28:54 +00:00
. \ ptrFiles . ps1 -Action reconcile -Path " .\targetdir "
2021-07-10 04:27:55 +00:00
. Example
# Pack and encrypt all files in folder ".\transferpack\" using a password
2021-07-10 04:28:54 +00:00
. \ ptrFiles . ps1 -Action pack -Path " .\transferpack\ " -SecretKey " fjks932c-x=23ds "
2021-07-10 04:27:55 +00:00
. Example
# Unpack all files in 7ZIP file "transfer_protect_yyyMMdd_hhmm.7z" to folder ".\targetdir" using a password
2021-07-11 05:21:50 +00:00
. \ ptrFiles . ps1 -Action unpack -ArchiveFileName " transfer_protect_yyyMMdd_hhmm.7z " -Path " .\targetdir " -SecretKey " fjks932c-x=23ds "
2021-07-10 04:27:55 +00:00
. Example
# Pack and encrypt all files in folder ".\transferpack\02*" where the folder name starts with "02" using a password
2021-07-10 04:28:54 +00:00
. \ ptrFiles . ps1 -Action pack -Path " .\transferpack\02* " -SecretKey " fjks932c-x=23ds "
2021-07-10 04:27:55 +00:00
#>
2021-07-10 04:21:47 +00:00
param (
[ Parameter ( Mandatory ) ] [ String ] $Action ,
[ Parameter ( Mandatory ) ] [ String ] $Path ,
2021-07-10 04:25:15 +00:00
[ String ] $RecipientKeyName ,
[ String ] $SecretKey ,
2021-07-11 05:21:50 +00:00
[ String ] $ArchiveFileName ,
2021-07-10 04:27:55 +00:00
[ String ] $RootFolderName ,
2021-07-10 04:28:54 +00:00
[ String ] $FileFilter ,
2021-07-10 04:25:15 +00:00
[ String ] $ReconcileFileName ,
[ String ] $SecretFileName ,
2021-07-10 04:27:55 +00:00
[ switch ] $ExcludeHash ,
[ String ] $LogPath
2021-07-10 04:27:22 +00:00
2021-07-10 04:21:47 +00:00
)
$default_dateLocal = Get-Date -Format " yyyyMMdd_HHmm "
2021-07-10 04:28:54 +00:00
$default_archiveFile = " .\ptr_file_##date##.7z "
$default_reconcileFile = " ##protect_transfer_reconcile_files##.csv "
2021-07-10 04:21:47 +00:00
$default_secretEncrypted = " .\transfer.key "
function Write-Log {
param (
[ String ] $LogEntry
)
$date = Get-Date -f " yyyy-MM-dd "
2021-07-10 04:27:55 +00:00
if ( $LogPath -eq " " )
{
$logPath = Join-Path -Path " .\ " -ChildPath " Logs "
}
2021-07-10 04:28:54 +00:00
$logName = " ptr_files_ $date .log "
2021-07-10 04:21:47 +00:00
$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 (
2021-07-10 04:27:22 +00:00
[ int ] $length = 20 ,
[ String ] $characters = " abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!()?}][{@#*+- " ,
[ switch ] $ConvertToSecureString
2021-07-10 04:21:47 +00:00
)
$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 ]
}
}
2021-07-10 04:27:55 +00:00
function Test-Files
{
Param (
[ Parameter ( Mandatory ) ] [ String ] $FolderName ,
2021-07-10 04:28:54 +00:00
[ String ] $FileFilter
2021-07-10 04:27:55 +00:00
)
Get-ChildItem $folderName -Recurse | Where-Object { ! $_ . PSIsContainer } | ForEach-Object {
return $true
}
return $false
}
2021-07-10 04:21:47 +00:00
2021-07-11 05:21:50 +00:00
function Get-ConvenientFileSize
{
Param (
[ Parameter ( Mandatory ) ] [ long ] $Size
)
if ( $totalFileSize -ge 1000000000000 ) {
$totalRightLabel = " TB "
$totalFileXbytes = [ math ] :: Round ( ( $size / 1000000000000 ) , 2 )
} else {
if ( $totalFileSize -ge 1000000000 ) {
$totalRightLabel = " GB "
$totalFileXbytes = [ math ] :: Round ( ( $size / 1000000000 ) , 2 )
} else {
if ( $totalFileSize -ge 1000000 ) {
$totalRightLabel = " MB "
$totalFileXbytes = [ math ] :: Round ( ( $size / 1000000 ) , 2 )
} else {
$totalRightLabel = " KB "
$totalFileXbytes = [ math ] :: Round ( ( $size / 1000 ) , 2 )
}
}
}
return $totalFileXbytes . ToString ( ) + " " + $totalRightLabel
}
2021-07-10 04:21:47 +00:00
# Reconcile
function Set-Reconcile
{
Param (
[ Parameter ( Mandatory ) ] [ String ] $ReconcileFile ,
[ Parameter ( Mandatory ) ] [ String ] $FolderName ,
2021-07-10 04:27:55 +00:00
[ String ] $RootFolderName ,
2021-07-10 04:28:54 +00:00
[ String ] $FileFilter ,
2021-07-10 04:27:22 +00:00
[ switch ] $Feedback = $false
2021-07-10 04:21:47 +00:00
)
if ( $reconcileFile -eq " " )
{
$reconcileFile = $default_reconcileFile
}
2021-07-11 05:21:50 +00:00
if ( $folderName . StartsWith ( " @ " ) ) {
If ( ! ( Test-Path -Path $folderName . Substring ( 1 ) ) ) {
Write-Log " File ' $( $folderName . Substring ( 1 ) ) ' does not exist "
Write-Host " File ' $( $folderName . Substring ( 1 ) ) ' does not exist " -ForegroundColor Red
Close-Log
Exit
}
} else {
If ( ! ( Test-Path -Path $folderName ) ) {
Write-Log " Folder ' $folderName ' does not exist "
Write-Host " Folder ' $folderName ' does not exist " -ForegroundColor Red
Close-Log
Exit
}
2021-07-10 04:21:47 +00:00
}
Write-Log " Generating reconciliation file ' $reconcileFile ' "
Write-Host " Generating reconciliation file ' $reconcileFile ' "
$totalFileCount = 0
$totalFileSize = 0
2021-07-10 04:27:55 +00:00
if ( $rootFolderName -eq " " ) {
$rootFolderName = $folderName
}
2021-07-11 05:21:50 +00:00
if ( $ExcludeHash ) {
$messageFrequency = 1000
} else {
$messageFrequency = 500
}
2021-07-10 04:27:55 +00:00
2021-07-11 05:21:50 +00:00
Set-Content -Path $reconcileFile -Value '"FullName","LastWriteTime","CreationTime","LastAccessTime","Length","Hash","ParentFolder","Object","Attributes","Extension"'
if ( $folderName . StartsWith ( " @ " ) ) {
Write-Log " Using @ file ' $( $folderName . Substring ( 1 ) ) ' "
Write-Host " Using @ file ' $( $folderName . Substring ( 1 ) ) ' "
Get-Content -Path $ ( $folderName . Substring ( 1 ) ) | ForEach-Object {
2021-07-11 09:05:44 +00:00
if ( $_ -ne " " ) {
If ( ! ( Test-Path -Path $_ ) ) {
Write-Log " Folder/file ' $( $_ ) ' does not exist "
Write-Host " Folder/file ' $( $_ ) ' does not exist " -ForegroundColor Red
}
else {
Get-ChildItem $_ -Filter $fileFilter -Recurse | Where-Object { ! $_ . PSIsContainer } | ForEach-Object {
$totalFilecount = $totalFileCount + 1
$totalFileSize = $totalFileSize + $_ . Length
if ( ( $totalFilecount % $messageFrequency ) -eq 0 ) {
Write-Log " Read $totalFileCount files and size $( Get-ConvenientFileSize -Size $totalFileSize ) . Currently at folder ' $( $_ . Directory ) ' "
Write-Host " Read $totalFileCount files and size $( Get-ConvenientFileSize -Size $totalFileSize ) . Currently at folder ' $( $_ . Directory ) ' "
}
if ( $ExcludeHash ) {
$sourceHash = " "
} else {
$sourceHash = ( Get-FileHash -Path $_ . FullName ) . Hash
}
$record = '"' + $_ . FullName . Replace ( $rootFolderName , " " ) + '","' + $_ . LastWriteTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) + '"'
$record = $record + ',"' + $_ . CreationTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) + '","' + $_ . LastAccessTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) + '"'
$record = $record + ',' + $_ . Length + ',"' + $sourceHash + '","' + $_ . Directory + '","' + $_ . Name + '","' + $_ . Attributes + '","' + $_ . Extension + '"'
Add-Content -Path $reconcileFile -Value $record
2021-07-11 05:21:50 +00:00
}
}
}
2021-07-10 04:21:47 +00:00
}
} else {
2021-07-11 05:21:50 +00:00
Get-ChildItem $folderName -Filter $fileFilter -Recurse | Where-Object { ! $_ . PSIsContainer } | ForEach-Object {
$totalFilecount = $totalFileCount + 1
$totalFileSize = $totalFileSize + $_ . Length
if ( ( $totalFilecount % $messageFrequency ) -eq 0 ) {
Write-Log " Read $totalFileCount files and size $( Get-ConvenientFileSize -Size $totalFileSize ) . Currently at folder ' $( $_ . Directory ) ' "
Write-Host " Read $totalFileCount files and size $( Get-ConvenientFileSize -Size $totalFileSize ) . Currently at folder ' $( $_ . Directory ) ' "
}
if ( $ExcludeHash ) {
$sourceHash = " "
2021-07-10 04:21:47 +00:00
} else {
2021-07-11 05:21:50 +00:00
$sourceHash = ( Get-FileHash -Path $_ . FullName ) . Hash
2021-07-10 04:21:47 +00:00
}
2021-07-11 05:21:50 +00:00
$record = '"' + $_ . FullName . Replace ( $rootFolderName , " " ) + '","' + $_ . LastWriteTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) + '"'
$record = $record + ',"' + $_ . CreationTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) + '","' + $_ . LastAccessTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) + '"'
$record = $record + ',' + $_ . Length + ',"' + $sourceHash + '","' + $_ . Directory + '","' + $_ . Name + '","' + $_ . Attributes + '","' + $_ . Extension + '"'
Add-Content -Path $reconcileFile -Value $record
2021-07-10 04:21:47 +00:00
}
2021-07-11 05:21:50 +00:00
2021-07-10 04:21:47 +00:00
}
2021-07-11 05:21:50 +00:00
Write-Log " Total reconcile file count is $totalFileCount and size $( Get-ConvenientFileSize -Size $totalFileSize ) ( $totalFileSize ) "
2021-07-10 04:21:47 +00:00
if ( $feedback ) {
2021-07-11 05:21:50 +00:00
Write-Host " Total reconcile file count is $totalFileCount and size $( Get-ConvenientFileSize -Size $totalFileSize ) " -ForegroundColor Green
}
}
function Invoke-SinglePack
{
Param (
[ String ] $ArchiveFolder ,
[ String ] $ArchiveFile ,
[ String ] $FileFilter ,
[ Boolean ] $FirstCompress
)
Write-Log " Archive folder ' $ArchiveFolder ' "
Write-Host " Archivefolder ' $ArchiveFolder ' "
if ( Test-Files -FolderName $ArchiveFolder -FileFilter $FileFilter ) {
try {
if ( $FirstCompress ) {
Compress - 7Zip -Path $ArchiveFolder -ArchiveFileName $ArchiveFile -Format SevenZip -PreserveDirectoryRoot -Filter $FileFilter
} else {
Compress - 7Zip -Path $ArchiveFolder -ArchiveFileName $ArchiveFile -Format SevenZip -PreserveDirectoryRoot -Filter $FileFilter -Append
}
$FirstCompress = $false
} catch {
Write-Log " Compress error with file ' $ArchiveFolder '. See any previous errors. $Error "
Write-Host " Compress error with file ' $ArchiveFolder '. See any previous errors. $Error " -ForegroundColor Red
}
} else {
Write-Log " Empty folder ' $ArchiveFolder ' "
Write-Host " Empty folder ' $ArchiveFolder ' "
2021-07-10 04:21:47 +00:00
}
2021-07-11 05:21:50 +00:00
return $FirstCompress
2021-07-10 04:21:47 +00:00
}
2021-07-11 05:21:50 +00:00
2021-07-10 04:27:55 +00:00
# Compress / Package
2021-07-10 04:21:47 +00:00
function Invoke-Pack
{
Param (
[ String ] $TransferFolder ,
2021-07-10 04:27:55 +00:00
[ String ] $RootFolder ,
2021-07-10 04:28:54 +00:00
[ String ] $FileFilter ,
2021-07-10 04:25:15 +00:00
[ String ] $Secret ,
2021-07-10 04:21:47 +00:00
[ String ] $CompressFile ,
2021-07-10 04:25:15 +00:00
[ String ] $ReconcileFile
2021-07-10 04:21:47 +00:00
)
2021-07-10 04:27:55 +00:00
2021-07-11 05:21:50 +00:00
if ( $transferFolder . StartsWith ( " @ " ) ) {
If ( ! ( Test-Path -Path $transferFolder . Substring ( 1 ) ) ) {
Write-Log " File ' $( $transferFolder . Substring ( 1 ) ) ' does not exist "
Write-Host " File ' $( $transferFolder . Substring ( 1 ) ) ' does not exist " -ForegroundColor Red
Close-Log
Exit
}
} else {
If ( ! ( Test-Path -Path $transferFolder ) ) {
Write-Log " Folder ' $transferFolder ' does not exist "
Write-Host " Folder ' $transferFolder ' does not exist " -ForegroundColor Red
Close-Log
Exit
}
2021-07-10 04:21:47 +00:00
}
2021-07-10 04:28:54 +00:00
Write-Log " Saving folders/files to archive file ' $compressFile ' "
Write-Host " Saving folders/files to archive file ' $compressFile ' "
2021-07-10 04:21:47 +00:00
if ( $reconcileFile -eq " " )
{
$reconcileFile = $default_reconcileFile
}
2021-07-10 04:28:54 +00:00
if ( $fileFilter -eq " " )
{
$fileFilter = " * "
}
2021-07-10 04:27:55 +00:00
if ( $transferFolder . EndsWith ( " * " ) )
{
2021-07-11 09:05:44 +00:00
Write-Log " Archive primary folder is ' $transferFolder ' "
2021-07-10 04:27:55 +00:00
$firstCompress = $true
Get-ChildItem $transferFolder | ForEach-Object {
2021-07-10 04:28:54 +00:00
Write-Log " Archive folder ' $( $_ . FullName ) ' "
Write-Host " Archivefolder ' $( $_ . FullName ) ' "
if ( Test-Files -FolderName $_ . FullName -FileFilter $fileFilter ) {
2021-07-10 04:27:55 +00:00
try {
if ( $firstCompress ) {
2021-07-10 04:28:54 +00:00
Compress - 7Zip -Path $_ . FullName -ArchiveFileName $compressFile -Format SevenZip -PreserveDirectoryRoot -Filter $fileFilter
2021-07-10 04:27:55 +00:00
} else {
2021-07-10 04:28:54 +00:00
Compress - 7Zip -Path $_ . FullName -ArchiveFileName $compressFile -Format SevenZip -PreserveDirectoryRoot -Filter $fileFilter -Append
2021-07-10 04:27:55 +00:00
}
$firstCompress = $false
} catch {
Write-Log " Compress error with file ' $( $_ . FullName ) '. See any previous errors. $Error "
Write-Host " Compress error with file ' $( $_ . FullName ) '. See any previous errors. $Error " -ForegroundColor Red
}
} else {
Write-Log " Empty folder ' $( $_ . FullName ) ' "
Write-Host " Empty folder ' $( $_ . FullName ) ' "
}
2021-07-10 04:27:22 +00:00
}
2021-07-10 04:27:55 +00:00
} else {
2021-07-11 05:21:50 +00:00
if ( $transferFolder . StartsWith ( " @ " ) ) {
Write-Log " Using @ file ' $( $transferFolder . Substring ( 1 ) ) ' "
Write-Host " Using @ file ' $( $transferFolder . Substring ( 1 ) ) ' "
Get-Content -Path $ ( $transferFolder . Substring ( 1 ) ) | ForEach-Object {
2021-07-11 09:05:44 +00:00
if ( $_ -ne " " ) {
If ( ! ( Test-Path -Path $_ ) ) {
Write-Log " Folder/file ' $( $_ ) ' does not exist "
Write-Host " Folder/file ' $( $_ ) ' does not exist " -ForegroundColor Red
}
else {
Write-Log " Archive folder ' $( $_ ) ' "
Write-Host " Archivefolder ' $( $_ ) ' "
if ( Test-Files -FolderName $_ -FileFilter $fileFilter ) {
try {
if ( $firstCompress ) {
Compress - 7Zip -Path $_ -ArchiveFileName $compressFile -Format SevenZip -PreserveDirectoryRoot -Filter $fileFilter
} else {
Compress - 7Zip -Path $_ -ArchiveFileName $compressFile -Format SevenZip -PreserveDirectoryRoot -Filter $fileFilter -Append
}
$firstCompress = $false
} catch {
Write-Log " Compress error with file ' $( $_ ) '. See any previous errors. $Error "
Write-Host " Compress error with file ' $( $_ ) '. See any previous errors. $Error " -ForegroundColor Red
2021-07-11 05:21:50 +00:00
}
2021-07-11 09:05:44 +00:00
} else {
Write-Log " Empty folder ' $( $_ . FullName ) ' "
Write-Host " Empty folder ' $( $_ . FullName ) ' "
2021-07-11 05:21:50 +00:00
}
}
}
}
} else {
Write-Log " Archive folder ' $transferFolder ' "
Write-Host " Archive folder ' $transferFolder ' "
Compress - 7Zip -Path $transferFolder -ArchiveFileName $compressFile -Format SevenZip -Filter $fileFilter
}
2021-07-10 04:27:22 +00:00
}
2021-07-10 04:27:55 +00:00
If ( ! ( Test-Path -Path $compressFile ) ) {
2021-07-10 04:28:54 +00:00
Write-Log " Archive file ' $compressFile ' was not created. See any previous errors "
Write-Host " Archive file ' $compressFile ' was not created. See any previous errors " -ForegroundColor Red
2021-07-10 04:27:55 +00:00
Close-Log
Exit
}
2021-07-10 04:28:54 +00:00
Set-Reconcile -ReconcileFile $reconcileFile -FolderName $transferFolder -FileFilter $fileFilter -RootFolderName $rootFolder
If ( ! ( Test-Path -Path $reconcileFile ) ) {
2021-07-10 04:27:55 +00:00
Write-Log " Reconcile file ' $reconcileFile ' was not created. See any previous errors "
Write-Host " Reconcile file ' $reconcileFile ' was not created. See any previous errors " -ForegroundColor Red
Close-Log
return
}
2021-07-10 04:21:47 +00:00
Write-Log " Add reconcile file ' $reconcileFile ' to file ' $compressFile ' "
2021-07-10 04:27:55 +00:00
$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
2021-07-10 04:25:15 +00:00
2021-07-10 04:28:54 +00:00
Write-Log " Archive file ' $compressFile ' created from folder ' $transferFolder ' "
Write-Host " Archive file ' $compressFile ' created from folder ' $transferFolder ' " -ForegroundColor Green
2021-07-10 04:21:47 +00:00
}
function Invoke-Unpack
{
Param (
[ String ] $RestoreFolder ,
2021-07-10 04:25:15 +00:00
[ String ] $Secret ,
[ String ] $CompressFile
2021-07-10 04:21:47 +00:00
)
2021-07-10 04:25:15 +00:00
2021-07-10 04:21:47 +00:00
If ( ! ( Test-Path -Path $CompressFile ) ) {
2021-07-10 04:28:54 +00:00
Write-Log " Archive file ' $CompressFile ' does not exist "
Write-Host " Archive file ' $CompressFile ' does not exist " -ForegroundColor Red
2021-07-10 04:21:47 +00:00
Close-Log
2021-07-10 04:27:55 +00:00
Exit
2021-07-10 04:21:47 +00:00
}
Write-Log " Restoring files transferred to ' $restoreFolder ' "
2021-07-10 04:28:54 +00:00
Write-Log " Archive file is ' $compressFile ' "
2021-07-10 04:21:47 +00:00
2021-07-10 04:25:15 +00:00
# Uncompress the data files
2021-07-10 04:21:47 +00:00
Expand - 7Zip -ArchiveFileName $compressFile -TargetPath $restoreFolder -Password $secret
2021-07-10 04:28:54 +00:00
Write-Log " Contents unpacked from archive file ' $compressFile ' to folder ' $restoreFolder ' "
Write-Host " Contents unpacked from archive file ' $compressFile ' to folder ' $restoreFolder ' " -ForegroundColor Green
2021-07-10 04:21:47 +00:00
}
# Reconcile
function Invoke-Reconcile
{
Param (
2021-07-10 04:27:55 +00:00
[ Parameter ( Mandatory ) ] [ String ] $ReconcileFile ,
[ Parameter ( Mandatory ) ] [ String ] $Folder ,
2021-07-11 09:05:44 +00:00
[ String ] $TargetReconcileFile ,
[ Switch ] $ExtendedCheck
2021-07-10 04:21:47 +00:00
)
2021-07-10 04:27:55 +00:00
if ( $reconcileFile -eq " " )
2021-07-10 04:25:15 +00:00
{
2021-07-10 04:27:55 +00:00
$reconcileFile = $default_reconcileFile
2021-07-10 04:25:15 +00:00
}
2021-07-10 04:21:47 +00:00
Write-Log " Reconciling documents transferred "
Write-Host " Reconciling documents transferred "
2021-07-10 04:27:55 +00:00
If ( ! ( Test-Path -Path $reconcileFile ) ) {
Write-Log " Reconciliation file ' $reconcileFile ' does not exist "
Write-Host " Reconciliation file ' $reconcileFile ' does not exist " -ForegroundColor Red
2021-07-10 04:21:47 +00:00
Close-Log
2021-07-10 04:27:55 +00:00
Exit
2021-07-10 04:21:47 +00:00
}
2021-07-10 04:27:55 +00:00
If ( ! ( Test-Path -Path $folder ) ) {
Write-Log " Folder ' $folder ' does not exist "
Write-Host " Folder ' $folder ' does not exist " -ForegroundColor Red
2021-07-10 04:21:47 +00:00
Close-Log
2021-07-10 04:27:55 +00:00
Exit
2021-07-10 04:21:47 +00:00
}
2021-07-10 04:27:55 +00:00
Write-Log " Using reconciliation file ' $reconcileFile ' "
2021-07-10 04:21:47 +00:00
2021-07-10 04:27:55 +00:00
$totalFileCount = 0
$totalFileSize = 0
2021-07-10 04:21:47 +00:00
$errorCount = 0
2021-07-11 09:05:44 +00:00
$missingFileCount = 0
2021-07-10 04:27:55 +00:00
$missingHash = $false
2021-07-10 04:21:47 +00:00
# For each entry in the reconcile file
# find the file and compare hash
2021-07-10 04:27:55 +00:00
Import-Csv $reconcileFile | ForEach-Object {
2021-07-10 04:21:47 +00:00
$totalFileCount = $totalFileCount +1
2021-07-10 04:27:55 +00:00
$restoreFileName = $ ( Join-Path -Path $folder -ChildPath $_ . FullName )
2021-07-10 04:21:47 +00:00
If ( Test-Path -Path $restoreFileName ) {
2021-07-10 04:27:55 +00:00
if ( $_ . Hash -ne " " ) {
$targetHash = ( Get-FileHash -Path $restoreFileName ) . Hash
if ( $_ . Hash -ne $targetHash ) {
$errorCount = $errorCount + 1
2021-07-11 09:05:44 +00:00
Write-Log " Hash mismatch for file ' $restoreFileName ' with target value $targetHash "
2021-07-10 04:27:55 +00:00
}
} else {
$missingHash = $true
2021-07-10 04:21:47 +00:00
}
2021-07-11 09:05:44 +00:00
if ( ( Get-Item -Path $restoreFileName ) . CreationTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) -ne $_ . CreationTime ) {
$errorCount = $errorCount + 1
Write-Log " Creation mismatch for file ' $restoreFileName ' with target value $( ( Get-Item -Path $restoreFileName ) . CreationTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) ) "
}
if ( ( Get-Item -Path $restoreFileName ) . Length -ne $_ . Length ) {
2021-07-10 04:21:47 +00:00
$errorCount = $errorCount + 1
2021-07-11 09:05:44 +00:00
Write-Log " Length mismatch for file ' $restoreFileName ' with target value $( Get-Item -Path $restoreFileName ) .Length) "
}
# Note that last / write access time is not checked by default as it will comonly be changed after restore
if ( $extendedCheck ) {
if ( ( Get-Item -Path $restoreFileName ) . LastAccessTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) -ne $_ . LastAccessTime ) {
$errorCount = $errorCount + 1
Write-Log " Last access mismatch for file ' $restoreFileName ' with target value $( ( Get-Item -Path $restoreFileName ) . LastAccessTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) ) "
}
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 ' with target value $( ( Get-Item -Path $restoreFileName ) . LastWriteTime . ToString ( " yyyy-MM-ddTHH:mm:ss " ) ) "
}
2021-07-10 04:21:47 +00:00
}
2021-07-11 09:05:44 +00:00
2021-07-10 04:27:55 +00:00
$totalFileSize = $totalFileSize + ( Get-Item -Path $restoreFileName ) . Length
2021-07-10 04:21:47 +00:00
} else {
2021-07-11 09:05:44 +00:00
$missingFileCount = $missingFileCount + 1
2021-07-10 04:21:47 +00:00
$errorCount = $errorCount + 1
Write-Log " Non existant target file ' $restoreFileName ' "
}
}
2021-07-11 05:21:50 +00:00
Write-Log " Total file storage size is $( Get-ConvenientFileSize -Size $totalFileSize ) ( $totalFileSize ) "
Write-Host " Total file storage size is $( Get-ConvenientFileSize -Size $totalFileSize ) "
2021-07-10 04:27:55 +00:00
if ( $missingHash )
{
Write-Log " Reconcile file had one or many or all blank hash entries "
Write-Host " Reconcile file had one or many or all blank hash entries " -ForegroundColor Yellow
}
2021-07-11 09:05:44 +00:00
Write-Log " Total file count is $totalFileCount with $errorCount errors "
Write-Log " There are $missingFileCount missing files "
2021-07-10 04:21:47 +00:00
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
}
2021-07-11 09:05:44 +00:00
if ( $missingFileCount -gt 0 ) {
Write-Host " There are $missingFileCount missing files " -ForegroundColor Red
}
2021-07-10 04:21:47 +00:00
}
$dateTimeStart = Get-Date -f " yyyy-MM-dd HH:mm:ss "
Write-Log " *********************************************************************************** "
Write-Log " * Start of processing: [ $dateTimeStart ] "
Write-Log " *********************************************************************************** "
$actioned = $false
2021-07-11 05:21:50 +00:00
Write-Log " Script parameters follow "
ForEach ( $boundParam in $PSBoundParameters . GetEnumerator ( ) )
{
Write-Log " Parameter: $( $boundParam . Key ) Value: $( $boundParam . Value ) "
# 'Key={0} Value={1}' -f $boundParam.Key, $boundParam.Value | Write-Log
}
Write-Log " "
2021-07-10 04:27:22 +00:00
if ( $action -eq " Install " ) {
$actioned = $true
Install-Module -Name 7Zip4Powershell -Scope CurrentUser
}
2021-07-10 04:21:47 +00:00
if ( $action -eq " Pack " ) {
$actioned = $true
2021-07-10 04:25:15 +00:00
if ( ( $RecipientKeyName -eq " " ) -and ( $SecretKey -eq " " ) ) {
Write-Log " Recipient Key Name or Secret Key required for packing "
Write-Host " Recipient Key Name or Secret Key required for packing " -ForegroundColor Red
2021-07-10 04:21:47 +00:00
Close-Log
return
}
2021-07-10 04:27:55 +00:00
if ( $rootFolderName -eq " " ) {
if ( $path . EndsWith ( " * " ) ) {
Write-Log " Root folder required for packing when using wild card for Path "
Write-Host " Root folder required for packing when using wild card for Path " -ForegroundColor Red
Close-Log
return
} else {
$rootFolderName = $path
}
}
2021-07-11 05:21:50 +00:00
if ( $ArchiveFileName -eq " " ) {
$ArchiveFileName = $default_archiveFile . Replace ( " ##date## " , $default_dateLocal )
2021-07-10 04:21:47 +00:00
}
2021-07-10 04:25:15 +00:00
if ( $SecretKey -eq " " ) {
if ( $secretFileName -eq " " )
{
$secretFileName = $default_secretEncrypted
}
$secret = New-RandomPassword -Length 80
Protect-CmsMessage -To $recipientKeyName -OutFile $secretFileName -Content $secret
} else {
$secret = $SecretKey
}
2021-07-11 05:21:50 +00:00
Invoke-Pack -TransferFolder $path -Secret $secret -CompressFile $ArchiveFileName -ReconcileFile $reconcileFileName -RootFolder $rootFolderName -FileFilter $fileFilter
2021-07-10 04:21:47 +00:00
}
if ( $action -eq " Unpack " ) {
$actioned = $true
2021-07-10 04:25:15 +00:00
if ( ( $RecipientKeyName -eq " " ) -and ( $SecretKey -eq " " ) ) {
Write-Log " Recipient Key Name or Secret Key required for unpacking "
Write-Host " Recipient Key Name or Secret Key required for unpacking " -ForegroundColor Red
2021-07-10 04:21:47 +00:00
Close-Log
return
}
2021-07-11 05:21:50 +00:00
if ( $ArchiveFileName -eq " " ) {
2021-07-10 04:28:54 +00:00
Write-Log " Archive file Name required for unpacking "
Write-Host " Archive file Name required for unpacking " -ForegroundColor Red
2021-07-10 04:21:47 +00:00
Close-Log
return
}
2021-07-10 04:25:15 +00:00
if ( $SecretKey -eq " " ) {
if ( $secretFileName -eq " " )
{
$secretFileName = $default_secretEncrypted
}
$secret = Unprotect-CmsMessage -To $recipientKeyName -Path $secretFileName
} else {
$secret = $SecretKey
}
2021-07-11 05:21:50 +00:00
Invoke-Unpack -RestoreFolder $path -Secret $secret -CompressFile $ArchiveFileName
2021-07-10 04:21:47 +00:00
}
if ( $action -eq " ReconcileFile " ) {
$actioned = $true
2021-07-10 04:25:15 +00:00
if ( $reconcileFileName -eq " " )
{
$reconcileFileName = $default_reconcileFile
}
2021-07-10 04:28:54 +00:00
Set-Reconcile -ReconcileFile $reconcileFileName -FolderName $path -Feedback -RootFolderName $rootFolderName -FileFilter $fileFilter
2021-07-10 04:21:47 +00:00
}
if ( $action -eq " Reconcile " ) {
$actioned = $true
2021-07-10 04:25:15 +00:00
if ( $reconcileFileName -eq " " )
{
$reconcileFileName = $default_reconcileFile
}
2021-07-10 04:21:47 +00:00
$localReconcileFile = Join-Path -Path $path -ChildPath $reconcileFileName
2021-07-10 04:27:55 +00:00
Invoke-Reconcile -ReconcileFile $localReconcileFile -Folder $path
2021-07-10 04:21:47 +00:00
}
2021-07-11 09:05:44 +00:00
if ( $action -eq " ArchiveInformation " ) {
$actioned = $true
if ( ( $RecipientKeyName -eq " " ) -and ( $SecretKey -eq " " ) ) {
Write-Log " Recipient Key Name or Secret Key required for 7Zip information "
Write-Host " Recipient Key Name or Secret Key required for 7Zip information " -ForegroundColor Red
Close-Log
return
}
if ( $SecretKey -eq " " ) {
if ( $secretFileName -eq " " )
{
$secretFileName = $default_secretEncrypted
}
$secret = Unprotect-CmsMessage -To $recipientKeyName -Path $secretFileName
} else {
$secret = $SecretKey
}
Write-Log " Retrieving archive information "
Write-Host " Retrieving archive information "
Get - 7ZipInformation -ArchiveFileName $ArchiveFileName -Password $secret
}
2021-07-10 04:21:47 +00:00
if ( ! ( $actioned ) )
{
Write-Log " Unknown action ' $action '. No processing performed "
Write-Host " Unknown action ' $action '. No processing performed " -ForegroundColor Red
Write-Host " Recognised actions: "
2021-07-11 09:05:44 +00:00
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 "
Write-Host " Install : Install required packages "
Write-Host " ArchiveInformation : Fetch archive information from archive file "
2021-07-11 05:21:50 +00:00
Write-Host " "
Write-Host " For help use command "
Write-Host " Get-Help .\ptrFiles.ps1 "
2021-07-10 04:21:47 +00:00
}
Close-Log