Using PowerShell to export Bitwarden vaults Matt B, May 20, 2024May 21, 2024 Table of Contents IntroWhat problem are we trying to solve?but I just want the script!Things to considerPart 1: Export Bitwarden vaultsPart 2: Password protectionPart 3: Moving them to the NASPart 4: FutureWrapping upIntroMy partner and I both use Bitwarden to store our passwords, and as we like to err on the side of paranoia, we take a backup each month and store this both on Google Drive and on our NAS. However, this takes quite a while to do, and so I wanted to find a way to export Bitwarden vaults programatically. I was able to achieve this by using the Bitwarden CLI and a PowerShell script.This script not only creates backups of our vaults, but it also stores them on our NAS. This guide will run through how the script works and how you can adapt it for your own use.What problem are we trying to solve?Getting an export of a Bitwarden vault via the web interface is a fairly straightforward task. You sign in, navigate around the UI, enter your password a few times and get a JSON export. However, depending on your preference for storing this eslewhere, you may have more steps such as:Zipping the JSON filePassword protecting the zipped JSON fileMoving the backup to other storage mediumsRemoving the JSON file afterwardsTesting you used the correct password when creating the ZIPWhen you’re doing this for 2 accounts, and you like to do it monthly, it quickly becomes a bit of a timedrain. It’s not awful, but it is time I could reclaim to use elsewhere. This is why I wanted to create a script to automate the majority of this process.but I just want the script!If you don’t want to read the article and just want the script, here you go:$env:BW_CLIENTSECRET='REPLACE THIS WITH YOUR SECRET' $env:BW_CLIENTID='REPLACE THIS WITH YOUR ID' $masterPassword=(Get-Content -Path ./config/password.txt) $currentYear = Get-Date -Format yyyy $currentMonth = Get-Date -Format MMMM bw logout bw login --apikey echo $masterPassword | bw unlock echo $masterPassword | bw export --output "./name.json" --format json bw lock bw logout echo($masterPassword) | & "C:\Program Files\7-Zip\7z.exe" a Name.zip name.json -p Remove-Item ./name.json if(-not(Test-Path "\\NASIP\PATH\TOBACKUPS\$currentYear\$currentMonth\")) { New-Item "\\NASIP\PATH\TOBACKUPS\\$currentYear\$currentMonth\Name" -ItemType Directory -Force } Copy-Item ./Name.zip -Destination "\\NASIP\PATH\TOBACKUPS\\$currentYear\$currentMonth\Name"Remember to create a config directory in the same directory as the script, and place a password.txt file in there, containing the password. You need to also ensure that the Bitwarden CLI is installed. Things to considerIf you’re going to use this script, please keep in mind the following things:This script requires you to store your master password in a plaintext file – do this at your own risk! We are only running this on our local machine but if you’re planning on running this on a server somewhere, you’ll want to come up with something more secure.You need to install the Bitwarden CLI to get this to work. Guides for doing so can be found online.You don’t have to ZIP the files with a password, as Bitwarden does allow you to add a password to the exports manually. I opt to use a ZIP however as this allows me to access the unecrypted data should that be neccesary. I’m using 7Zip for the compression, which is what this script will use also.It is absolutely crucial you don’t allow your password, client ID or client secret to be known by anyone.Having read the above, we can now move on to creating the script.Part 1: Export Bitwarden vaultsIn order to authenticate with the Bitwarden CLI, we need to setup some environmental variables. The values for these can be found within your Bitwarden account by going to Settings -> Security -> Keys -> View API key.Once you have these, add the following to your PS script:$env:BW_CLIENTSECRET='REPLACE THIS WITH YOUR SECRET' $env:BW_CLIENTID='REPLACE THIS WITH YOUR ID'We also want to create a master password file, which is a txt file containing your master password. As mentioned above, if you’re using this on a system that can be accessed by anyone else, you’ll want to refactor this into something more secure. In my case, I created a directory called config and I then have a file called password.txt within. Make sure there is no additional whitespace in your file, and then bring this into your script:$masterPassword=(Get-Content -Path ./config/password.txt) Finally, I’m going to set up the below variables as later on in the script, I’ll be creating folders dynamically with a date naming convention:$currentYear = Get-Date -Format yyyy $currentMonth = Get-Date -Format MMMM Now we can move on to getting the exports. The process for getting the exports works like this:Make sure we are logged out to invalidate any old sessions -> Login with our API details -> Unlock the vault with our password -> Export the vault -> Relock the vault -> Sign outThe code for this is as below:bw logout bw login --apikey echo $masterPassword | bw unlock echo $masterPassword | bw export --output "./name.json" --format json bw lock bw logout Of course, name.json can be replaced with whatever you want to name the export file. Once the above commands have ran, we should have a file in our working directory called name.json that contains the (unencrypted) vault data.Part 2: Password protectionWhile I want access to the unencrypted vault data, I don’t want anyone who gets their hands on the file to be able to view it directly. As such, I’m going to zip the file up with a password.This can be achieved by using 7Zip. The below command will create a ZIP archive (named Name.zip), protected with the master password. We will then remove the raw JSON file afterwards so that it cannot be accessed:echo($masterPassword) | & "C:\Program Files\7-Zip\7z.exe" a Name.zip name.json -p Remove-Item ./name.jsonYou may need to change the path for 7Zip depending on where you have it installed. At this stage, we have now used the Bitwarden CLI and 7Zip to login, get an export of our vault, password protect it, and then remove the original export.Part 3: Moving them to the NASAs mentioned earlier, we like to store our backups on a NAS device that we own. Fortunately, this is very easy to achieve using PowerShell. We structure our backup folder like this:BitwardenBackups -2023 --December ---Person1 ---Person2 -2024 --January ---Person1 ---Person2 --February ---Person1 ---Person2With this format being in use, I’ve tailored the script so that when it moves the backup file to the NAS, it will check if a suitable folder for the date of the script execution exists. If one does not exist, it will be created, and the files will then be moved there. This is very easy to achieve using the $currentYear and $currentMonth variables that we configured earlier:if(-not(Test-Path "\\NASIP\PATH\TOBACKUPS\$currentYear\$currentMonth\")) { New-Item "\\NASIP\PATH\TOBACKUPS\\$currentYear\$currentMonth\Name" -ItemType Directory -Force } Copy-Item ./Name.zip -Destination "\\NASIP\PATH\TOBACKUPS\\$currentYear\$currentMonth\Name" If you are looking to export Bitwarden vaults for multiple people and have a second user in this script, be sure to drop the conditional check after the first person. Otherwise, as the year/month folder now exists, the New-Item cmdlet won’t run, and as such it won’t create a named folder for the second user. With this in place, our script now does the following:Logs to BitwardenGets an export of the vault, and save this as a JSON fileLogs of BitwardenCreates a password-protected ZIP containing that JSON fileRemoves the JSON fileCopies the ZIP backup to the NAS, in a dynamically-created folder, following a year/date naming conventionPart 4: FutureThis script is already saving us 15 minutes of hands on work each month, however we still have to manually create the year/month folders on Google Drive, and we then have to upload the files there manually, and then remove the ZIP files from the local machine. In future, I might try to add Google Drive functionality to this so that the entire process can be completely automated. If I manage to get that working, I’ll release a follow up to this guide.Wrapping upThis guide has shown you how to export a Bitwarden vault with PowerShell by using the BW CLI, and how to move that backup to a NAS . If you’ve got any questions or run into any problems, please feel free to leave a comment. PowerShell Security