PeterDocs/PeterDocs/PeterB2.ps1

367 lines
14 KiB
PowerShell

$default_reconcileFile = "##protect_transfer_reconcile_files##.csv"
$default_profile = "default"
$default_archiveFile = ".\ptr_file_##date##.7z"
function Get-B2ApiToken {
Param
(
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $AccountId,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $AccountKey
)
Begin
{
if(-not $AccountID -or -not $AccountKey)
{
[PSCredential]$b2Creds = Get-Credential -Message 'Enter your B2 account ID and application key below.'
try
{
[String]$AccountId = $b2Creds.GetNetworkCredential().UserName
[String]$AccountKey = $b2Creds.GetNetworkCredential().Password
}
catch
{
throw 'You must specify the account ID and application key.'
}
}
[String]$plainCreds = "${AccountId}:${AccountKey}"
[String]$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($plainCreds))
[Hashtable]$sessionHeaders = @{'Authorization'="Basic $encodedCreds"}
[Uri]$b2ApiUri = 'https://api.backblaze.com/b2api/v1/b2_authorize_account'
}
Process
{
try
{
$b2Info = Invoke-RestMethod -Method Get -Uri $b2ApiUri -Headers $sessionHeaders
[String]$script:SavedB2AccountID = $b2Info.accountId
[Uri]$script:SavedB2ApiUri = $b2Info.apiUrl
[String]$script:SavedB2ApiToken = $b2Info.authorizationToken
[Uri]$script:SavedB2DownloadUri = $b2Info.downloadUrl
$b2ReturnInfo = [PSCustomObject]@{
'AccountID' = $b2Info.accountId
'ApiUri' = $b2Info.apiUrl
'DownloadUri' = $b2Info.downloadUrl
'Token' = $b2Info.authorizationToken
}
return $b2ReturnInfo
}
catch
{
$errorDetail = $_.Exception.Message
Write-Error -Exception "Unable to authenticate with given API Key.`n`r$errorDetail" `
-Message "Unable to authenticate with given API Key.`n`r$errorDetail" -Category AuthenticationError
#throw "Unable to authenticate with given APIKey.`n`r$errorDetail"
throw $_
}
}
}
function Get-B2Bucket {
Param
(
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $ApiToken,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $AccountId,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $ApiUri,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $BucketHost
)
Begin
{
[Hashtable]$sessionHeaders = @{'Authorization'=$ApiToken}
[String]$sessionBody = @{'accountId'=$AccountID} | ConvertTo-Json
[Uri]$b2ApiUri = "$ApiUri/b2api/v1/b2_list_buckets"
}
Process
{
$b2Info = Invoke-RestMethod -Method Post -Uri $b2ApiUri -Headers $sessionHeaders -Body $sessionBody
foreach($info in $b2Info.buckets)
{
if ($bucketHost -eq $info.bucketName) {
$b2ReturnInfo = [PSCustomObject]@{
'BucketName' = $info.bucketName
'BucketID' = $info.bucketId
'BucketType' = $info.bucketType
'AccountID' = $info.accountId
}
return $b2ReturnInfo
}
}
return $null
}
}
function Get-B2UploadUri {
Param
(
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $BucketHost,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $FileName,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [Uri] $ApiUri,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $ApiToken
)
Begin
{
[Hashtable]$sessionHeaders = @{'Authorization'=$ApiToken}
[Uri]$b2ApiUri = "$ApiUri/b2api/v1/b2_get_upload_url"
}
Process
{
try
{
[String]$sessionBody = @{'bucketId'=$bucketHost} | ConvertTo-Json
$b2Info = Invoke-RestMethod -Method Post -Uri $b2ApiUri -Headers $sessionHeaders -Body $sessionBody
$b2ReturnInfo = [PSCustomObject]@{
'BucketId' = $b2Info.BucketId
'UploadUri' = $b2Info.uploadUrl
'Token' = $b2Info.authorizationToken
}
return $b2ReturnInfo
}
catch
{
$errorDetail = $_.Exception.Message
Write-Error -Exception "Unable to retrieve the upload uri.`n`r$errorDetail" `
-Message "Unable to retrieve the upload uri.`n`r$errorDetail" -Category ReadError
}
}
}
function Invoke-B2SUpload {
Param
(
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $BucketHost,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $TargetPath,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $FileName,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [Uri] $ApiUri,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $ApiToken
)
try
{
[String] $b2FileName = [System.Uri]::EscapeDataString($TargetPath)
[String] $b2FileMime = [System.Web.MimeMapping]::GetMimeMapping($fileName)
[String]$b2FileSHA1 = (Get-FileHash -Path $fileName -Algorithm SHA1).Hash
[String]$b2FileAuthor = (Get-Acl -Path $fileName).Owner
$b2FileAuthor = $b2FileAuthor.Substring($b2FileAuthor.IndexOf('\')+1)
[Hashtable]$sessionHeaders = @{
'Authorization' = $ApiToken
'X-Bz-File-Name' = $b2FileName
'Content-Type' = $b2FileMime
'X-Bz-Content-Sha1' = $b2FileSHA1
'X-Bz-Info-Author' = $b2FileAuthor
}
$b2Info = Invoke-RestMethod -Method Post -Uri $ApiUri -Headers $sessionHeaders -InFile $fileName
$b2ReturnInfo = [PSCustomObject]@{
'Name' = $b2Info.fileName
'FileInfo' = $b2Info.fileInfo
'Type' = $b2Info.contentType
'Length' = $b2Info.contentLength
'BucketID' = $b2Info.bucketId
'AccountID' = $b2Info.accountId
'SHA1' = $b2Info.contentSha1
'ID' = $b2Info.fileId
}
return $b2ReturnInfo
}
catch
{
$errorDetail = $_.Exception.Message
Write-Error -Exception "Unable to upload the file.`n`r$errorDetail" `
-Message "Unable to upload the file.`n`r$errorDetail" -Category InvalidOperation
}
}
function Invoke-B2SDownload {
Param
(
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $BucketHost,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $SourcePath,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $FileName,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [Uri] $ApiDownloadUri,
[Parameter(Mandatory)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String] $ApiToken
)
Begin
{
if(-not (Test-Path -Path $FileName -IsValid))
{
throw 'The file path given ($FileName) is not valid.`n`rThe file cannot be saved.'
}
[Hashtable]$sessionHeaders = @{'Authorization'=$ApiToken}
}
Process
{
[Uri]$b2ApiUri = "${ApiDownloadUri}file/$BucketHost/$SourcePath"
try
{
Invoke-RestMethod -Method Get -Uri $b2ApiUri -Headers $sessionHeaders -OutFile $FileName
}
catch
{
$errorDetail = $_.Exception.Message
Write-Error -Exception "Unable to upload the file.`n`r$errorDetail" `
-Message "Unable to upload the file.`n`r$errorDetail" -Category InvalidOperation
}
}
}
# Compress / Package
<#
.Synopsis
Packs a source folder(s) into an encrypted 7ZIP archive file
that can be securely transported to a remote lcoation or
even used as a secure permmanent backup.
PeterDocs : Protect, Transfer, Reconcile Document Files
.Description
Packages source folder contents into a 7ZIP file, adding a reconciliation
file to the 7ZIP file and then encrypting the contents. The source folder
is not altered and only read rights are required. A log file is written
at exceution to record activity.
.Parameter SourceFolder
The path to the files and folders to pack.
The path name can include a trailing * as a wildcard to only include a subset of
directories.
When using the trailing * for names, the filtering is only applied to immediate
folder names under the parent folder. The filter does not cascade to lower folders.
The path can be a local drive, mapped network drive or a network shared folder
location such as \\MediaStor\MyLibrary.
The source folder parameter can also be a file containing a list of paths, one per line.
To use a list file, prefix the source folder 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 "*"
.Parameter RecipientKey
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.
Note: Currently the script doe snot user Secure Strings
.Parameter ArchiveFile
The location and name of the 7ZIP file. If not supplied a default 7ZIP file name
will be generated in the current directory for the pack action.
The default name will take the form ".\transfer_protect_yyyyMMdd_hhmm.7z"
For unpack actions, the archive file name parameter is mandatory.
.Parameter RootFolder
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
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"
.Parameter ReconcileFile
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.
The default name is "##protect_transfer_reconcile_files##.csv"
.Parameter SecretFile
The secret file name is used with RecipientKey to secure the
internally generated password for the 7ZIP file. When unpacking the
7ZIP file you will need access to this file if RecipientKey
was used. If not supplied a default name is used. This file is
encrypted with RecipientKey.
The default name is the archive file name with postfix ".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 functionality 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.
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.
The following environment variables are supported:
- PETERDOCS_RECIPIENTKEY
.Example
# Pack and encrypt all files in folder ".\transferpack\" using a private-public key
# A file with the postifx ".key" is also generated alongside the 7ZIP file
Invoke-Pack -SourceFolder ".\transferpack\" -RecipientKeyName data@mycompany
#>