Here’s a one-liner for backing up the BitLocker Recovery Key for every drive attached to the machine:


Followed by 9kb of more of the same.  It’s an EncodedCommand, so we can toss this into a scheduled task.  The actual encoded command is attached (thank goodness!) It outputs a .CLIXML file that contains a scalar or array of [Hashtable] with computer’s name, drive name (e.g. C:), drive id (a GUID) and the Recovery Key.

Here is the source code:

function Backup-BitLockerRecoverKey
    param (
        $Path = "$home\OneDrive\BitlockerRecovery\$env:Computername.$((Get-WmiObject -Class Win32_Computersystem).Domain).txt".ToLower()

    trap { 
        if ($DebugPreference -eq 'Continue') { $Host.EnterNestedPrompt(); }
        return ( Write-Warning $_.Exception.Message ); 
    $ErrorActionPreference = 'SilentlyContinue';
    if ( # effectively "is user running as Administrator"
        (New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentList (
        # create subfolders to path.
        if (Test-Path -Path $Path)
            $oldPath = "$($Path -replace '\.txt$').$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').txt";
            Move-Item -Path $Path -Destination $oldPath;

        } # if (Test-Path -Path $Path)
        elseif (!(Test-Path -Path (Split-Path -Path $Path -Parent)))
            New-Item -Path (
                Split-Path -Path $Path -Parent
            ) -ItemType Directory -ErrorAction SilentlyContinue -ErrorVariable errorVariable; 
        } # if (Test-Path -Path $Path)
        if ($errorVariable) { return ( Write-Warning $errorVariable.Exception.Message ); }
        Import-Module -Name Bitlocker -ErrorAction SilentlyContinue -ErrorVariable errorVariable;
        if ($errorVariable) { return ( Write-Warning $errorVariable.Exception.Message ); }

        Get-BitLockerVolume |
        % {
            $volumeData = $_;
            $volumeData.KeyProtector |
            ? { $_.RecoveryPassword } |
            % {            
                    ComputerName = $env:COMPUTERNAME;
                    Drive        = $volumeData.MountPoint;
                    GUID         = $_.KeyProtectorId -replace '[{}]';
                    Key          = $_.RecoveryPassword;
            } # $VolumeData.KeyProtector ...
        } | Tee-Object -Variable recoveryKeys;
        if ($recoveryKeys) { Export-Clixml -Path $Path -InputObject $recoveryKeys; }

        if (Test-Path -Path $Path)
            Write-Host -ForegroundColor Green "Created file $((Resolve-Path -Path $Path).ProviderPath)";
            if ($oldPath)
                if (!(Compare-Object -ReferenceObject (
                    Get-Content -Path $Path
                ) -DifferenceObject (
                    Get-Content -Path $oldPath
                    Remove-Item -Path $oldPath;
                    Write-Verbose -Message "Backup and new file identical. Removing $oldPath";
                } # if ((Get-Content -Path $Path) -eq (Get-Content -Path $oldPath))
                    Write-Warning -Message "'$Path' and '$oldPath' differ!";
                } # if ((Get-Content -Path $Path) -eq (Get-Content -Path $oldPath))
            } # if ($oldPath)
        } # if (Test-Path -Path $Path)
        elseif ($oldPath -and (Test-Path -Path $oldPath))
            Write-Warning -Message "Cannot create -Path '$Path'"; 
            Move-Item -Path $oldPath -Destination $Path;
        } # if (Test-Path -Path $Path) ... else
    } # if (<user is running as Administrator>)
    { Write-Warning -Message "Must be in a 'Run as Administrator' window."; }

} # function Backup-BitLockerRecoverKey


And here are the commands to generate that blob of text:

$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)

One thing to note: the source code and the encoded text are different.  For some reason, linefeeds don’t encode.  As much as I love clearly-commented code, a comment doesn’t work when it’s all slammed into one line, which is how the decoded string (yes, STRING) comes.