My primary device is corporate-owned, and contains a lot of corporate data.  Policy says to have it secured by BitLocker, so I’ll have it secured by BitLocker.  Perfectly reasonable, no?

However, I suspect something is wrong with this laptop’s TPM chip.  Every so often, it decides that it won’t accept my BitLocker PIN, and requires I have the recovery passcode, all 48 digits of it.  I save it to my SkyDrive (so I can read it via my phone if/when I need it), but sometimes I forget to do so.  Both as an academic exercise, and as a way making it possible for me to schedule it as a task, I wrote the script below.

The two key takeaways are:

- Get-BitLockerVolume gives the BitLocker recovery data

- Start-Process PowerShell.exe –Verb RunAs escalates permissions


<# .Synopsis
Save the bitlocker recovery key.

Save the bitlocker recovery key for the specified drive, elevating itself to Administrator if possible.  

The self-elevation is brittle as hell.  This script must be placed in a path that does not have special characters.  Specifically '$home\Skydrive @ Microsoft\foo' does NOT work.


param (
     [string]$Path = "$home\SkyDrive\BitlockerRecovery\$env:ComputerName-$(Get-Date -Format 'yyyy-MM-dd').txt",
     [string]$Drive = "c"

function Get-BitLockerRecoveryKey {
     param (
         [string]$Path = "$home\SkyDrive\BitlockerRecovery\$env:ComputerName-$(Get-Date -Format 'yyyy-MM-dd').txt",
         [string]$Drive = "c"

     # move existing key
     if (Test-Path -Path $path) { mv $path ("$path-$(Get-Date -Format yyyyMMddhhmmss).txt"); }

     # attempt to create destination folder if it doesn't exist
     $pathDirName = Split-Path -Parent -Path $Path;
     if (!(Test-Path -Path $pathDirName)) { New-Item -ItemType Directory -Path $path -ErrorAction SilentlyContinue | Out-null; }
     if (Test-Path -Path $pathDirName) {
         # If the folder exists, perform the heavy lifting
         $scriptBlock = {
             param (
                 [string]$Path = "$home\SkyDrive\BitlockerRecovery\$env:ComputerName-$(Get-Date -Format 'yyyy-MM-dd').txt",
                 [string]$Drive = "c"

             $KeyProtector = (Get-BitLockerVolume -ErrorAction SilentlyContinue -MountPoint $Drive).KeyProtector | ? { $_.KeyProtectorType -eq 'RecoveryPassword'; }
             if ($keyProtector) { "ID: $($KeyProtector.KeyProtectorId -replace '[{}]')`n`nKey: $($KeyProtector.RecoveryPassword)" | Out-File -FilePath $Path; }

         } # $scriptBlock =
         Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $Path, $Drive;
         if (Test-Path -path $Path) { 
             # if the file exists, this worked (but that's normally not the case)
         } else {
             # if the file doesn't exist, elevate itself.
             Start-Process PowerShell.Exe -WindowStyle Hidden -Verb RunAs -ArgumentList "-NoProfile -File $($MyInvocation.ScriptName) -Drive $Drive -Path $Path" -Wait;
             if (Test-Path -Path $Path) {
                 # if the file exists, this worked
             } else {
                 # otherwise, carp and exit
                 Write-Warning "Unable to save BitLocker recovery key for drive ${drive}:";
             } # if (Test-Path -Path $Path)

         } # if (Test-Path $Path)

     } else {
         # if output folder doesn't exist, carp and exit
         Write-Warning "Unable to find nor create $pathDirName";

     } # if (Test-Path -Path $pathDirName)

} # function

# run it.
Get-BitLockerRecoveryKey -Path $Path -Drive $Drive;