Files
bitwardenbackup/bitwardenBackup.ps1

159 lines
6.6 KiB
PowerShell

param (
[switch]$zip,
[switch]$encrypt,
[switch]$verbose
)
# Verbose output
if ($verbose) {
$VerbosePreference = "continue"
}
# get the date/time for the back filename
$dateTime = Get-Date -format ("yyyyMMdd-HHmmss")
# set the binaries path, do not rely on the path variables as they are not hashed
$bw = "$PSScriptRoot\lib\bw.exe"
$gpg = "$PSScriptRoot\lib\gpg\bin\gpg.exe"
$sdelete = "$PSScriptRoot\lib\sdelete.exe"
# set bitwarden server to my self hosted instance
#& $bw config server https://bitwarden.johnhgaunt.com
# begin while loop to login, if login is incorrect, ask user again
while ($true) {
# ask for api client id/secret and password
$clientID = Read-Host "Please enter your Bitwarden API client_id"
$env:BW_CLIENTID = "$clientID"
$clientSecret = Read-Host -assecurestring "Please enter your bitwarden API client_secret"
$clientSecret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($clientSecret))
$env:BW_CLIENTSECRET = "$clientSecret"
# test login
& $bw login --apikey --raw
$bwStatus = $(ConvertFrom-Json $(& $bw status))
if ($bwStatus."Status" -eq "locked") {
# Authentication was successful
# start new loop for password unlock
while ($true) {
$password = Read-Host -assecurestring "Please enter your Bitwarden password"
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$sessionKey = $(& $bw unlock $password --raw --nointeraction)
# get the bw status to see if the login was successfull and inform user
$bwStatus = $(ConvertFrom-Json $(& $bw status --session $sessionKey))
if ($bwStatus."Status" -eq "unlocked") {
$username = $bwStatus."userEmail"
break
} else {
# just writing a new line
Write-Host " "
Write-Warning "Unable to unlock your vault, please try agian."
}
}
break
} else {
Write-Host " "
Write-Warning "Unable to authenticate, please try agian."
}
}
# Export the vault to both CSV and JSON files, this allows best compatibility to import again or switch managers.
Write-Host "Exporting vault to both CSV and JSON files."
Write-Verbose "Exporting vault to CSV."
& $bw export $password --output "$PSScriptRoot\Bitwarden User $username Export $dateTime.csv" --format csv --session $sessionKey
# just writing a new line
Write-Host " "
Write-Verbose "Exporting vault to JSON."
& $bw export $password --output "$PSScriptRoot\Bitwarden User $username Export $dateTime.json" --format json --session $sessionKey
# just writing a new line
Write-Host " "
# look for organizations
Write-Host "Looking for Organizations..."
$organizations = $(ConvertFrom-Json $(& $bw list organizations --session $sessionKey))
Write-Host "Found $(($organizations | Measure-Object).count) Organiztaions."
# loop through the found organizations and again export both as CSV and JSON for best compatibility
$organizations | ForEach-Object {
Write-Host "Exporting organization $($_.name) vault to both CSV and JSON files."
Write-Verbose "Exporting organization vault to CSV."
& $bw export $password --organizationid $_.id --output "$PSScriptRoot\Bitwarden Organization $($_.name) Export $dateTime.csv" --format csv --session $sessionKey
# just writing a new line
Write-Host " "
Write-Verbose "Exporting organization vault to JSON."
& $bw export $password --organizationid $_.id --output "$PSScriptRoot\Bitwarden Organization $($_.name) Export $dateTime.json" --format json --session $sessionKey
# just writing a new line
Write-Host " "
}
# find all items with attachments
Write-Host "Looking for items with attachments..."
$itemsWithAttachments = $((ConvertFrom-Json $(& $bw list items --session $sessionKey)) | Where-Object attachments)
Write-Host "Found $(($itemsWithAttachments | Measure-Object).count) items with attachments."
# loop through all the items with attachments and download them into a folder with the name of the item
Write-Host "Downloading attachments..."
$itemsWithAttachments | ForEach-Object {
Write-Verbose "Working on item $($_.name) ($($_.id))."
$folder="$PSScriptRoot\attachments\$($_.name)"
$itemID=$_.id
$_.attachments | ForEach-Object {
Write-Verbose "Downloading attachment ($($_.id)) with name $($_.fileName) to $folder."
& $bw get attachment $_.id --itemid $itemID --output "$folder\$($_.fileName)" --session $sessionKey
# just writing a new line
Write-Host " "
Start-Sleep -Milliseconds 500
}
}
# zip file name used below
$zipFilename = "Bitwarden Backup $dateTime.zip"
# if the user wants the export zipped or encrypted
if ($zip -or $encrypt) {
# zip the export
Write-Host "Zipping the backup together..."
Compress-Archive -Path $PSScriptRoot\*.csv, $PSScriptRoot\*.json, $PSScriptRoot\attachments -DestinationPath "$PSScriptRoot\$zipFilename"
# securely delete the export items with sdelete
Write-Host "Securely deleting the exports and attachments..."
& $sdelete -s -p 25 $PSScriptRoot\*.csv $PSScriptRoot\*.json $PSScriptRoot\attachments
# if encrypting the export
if ($encrypt) {
# encrypt the zip export with gpg
Write-Host "Encrypting the backup zip with your bitwarden password..."
& $gpg --no-options --batch --passphrase "$password" --symmetric --cipher-algo AES256 --digest-algo SHA512 --compression-algo Uncompressed --output "$PSScriptRoot\$zipFilename.gpg" "$PSScriptRoot\$zipFilename"
# securely delete the zip export with sdelete
Write-Host "Securely deleting the zip file..."
& $sdelete -p 25 "$PSScriptRoot\$zipFilename"
}
}
# logout of bitwaren to ensure the session is destroyed
& $bw logout
# remove all the variables
Remove-Variable -Name * -ErrorAction SilentlyContinue
<#
# Restore of attachements
cd C:\users\jgaunt\Git\bitwardenbackup\attachments
bw status
$folders = Get-childItem -Directory
$items = bw list items | convertfrom-json
$array = @()
foreach ($folder in $folders) {
foreach ($item in $items) {
if ($folder.name -eq $item.name -and $item.type -gt 1) {
#$array += bw get item $item.id | convertfrom-json
$attachements = Get-ChildItem $folder
foreach ($attachement in $attachements) {
bw create attachment --file "$($attachement.FullName)" --itemid $item.id
}
Remove-Item -Recurse $folder
}
}
}
#>