param ( [switch]$zip, [switch]$encrypt, [switch]$verbose ) # Verbose output if ($verbose) { $oldverbose = $VerbosePreference $VerbosePreference = "continue" } # get the date/time for the back filename $dateTime = get-date -format ("yyyyMMdd-HHmmss") $env:Path += ";$PSScriptRoot\lib;$PSScriptRoot\lib\gpg\bin;$PSScriptRoot\lib\age" bw config server https://bitwarden.johnhgaunt.com while ($true) { $username = Read-Host "Please enter your bitwarden email" $password = Read-Host -assecurestring "Please enter your bitwarden password" $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)) $code = Read-Host "Please enter your 2fa code (hit enter if not used)" if ($code -eq "") { $sessionKey = $(bw login $username $password --raw --nointeraction) } else { $sessionKey = $(bw login $username $password --method 0 --code $code --raw --nointeraction) } $bwStatus = $(ConvertFrom-Json $(bw status)) if ($bwStatus.Status -ne "locked") { # just writing a new line Write-Host " " Write-Warning "Unable to login, please try agian." } else { break } } 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 " " Write-Host "Looking for Organizations..." $organizations = $(ConvertFrom-Json $(bw list organizations --session $sessionKey)) Write-Host "Found $(($organizations | measure).count) Organiztaions." $organizations | foreach { 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 " " } Write-Host "Looking for items with attachments..." $itemsWithAttachments = $((ConvertFrom-Json $(bw list items --session $sessionKey)) | Where-Object attachments) Write-Host "Found $(($itemsWithAttachments | measure).count) items with attachments." Write-Host "Downloading attachments..." $itemsWithAttachments | foreach { Write-Verbose "Working on item $($_.name) ($($_.id))." $folder="$PSScriptRoot\attachments\$($_.name)" $itemID=$_.id $_.attachments | foreach { 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 " " sleep -Milliseconds 500 } } $zipFilename = "Bitwarden Backup $dateTime.zip" if ($zip -or $encrypt) { Write-Host "Zipping the backup together..." Compress-Archive -Path $PSScriptRoot\*.csv, $PSScriptRoot\*.json, $PSScriptRoot\attachments -DestinationPath "$PSScriptRoot\$zipFilename" Write-Host "Securely deleting the exports and attachments..." sdelete64.exe -s -p 25 $PSScriptRoot\*.csv $PSScriptRoot\*.json $PSScriptRoot\attachments if ($encrypt) { Write-Host "Encrypting the backup zip with your bitwarden password..." gpg.exe --batch --passphrase "$password" --symmetric --cipher-algo AES256 --digest-algo SHA512 --compression-algo Uncompressed --output "$PSScriptRoot\$zipFilename.gpg" "$PSScriptRoot\$zipFilename" Write-Host "Securely deleting the zip file..." sdelete64.exe -p 25 "$PSScriptRoot\$zipFilename" } } Remove-Variable -Name * -ErrorAction SilentlyContinue bw logout