BitLocker management with Azure Table Storage

The following PowerShell script will get the local BitLocker-Recovery-Key and stores it in an Azure Table Storage. You can run this script from any System-Management Tool (e.g. ConfigMgr, Intune, DeviceCommander etc. ) to have a common data-store for BitLocker-Recovery-Keys.

Note: this script requires local admin rights to run !!!

$storageAccount = "xxx" #Enter your Storage Account here !!
$sasToken = "?sv=2018-03-28&si=BitLocker-Update&tn=bitlocker&sig=xS%2F6gEy1DFu8YHoztWkFGQfQwfOoLzHu0NtIDdufOYw%3D"

function Upload-BitLockerKey($TableName, $PartitionKey, $RowKey, $entity) {
    $version = "2017-04-17"
    $resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')$sasToken"
    $table_url = "https://$storageAccount.table.core.windows.net/$resource"
    $GMTTime = (Get-Date).ToUniversalTime().toString('R')
    $headers = @{
        'x-ms-date'    = $GMTTime
        "x-ms-version" = $version
        Accept         = "application/json;odata=fullmetadata"
    }
    $body = $entity | ConvertTo-Json
    Invoke-RestMethod -Method PUT -Uri $table_url -Headers $headers -Body $body -ContentType application/json
}

Get-BitLockerVolume | Where-Object {$_.KeyProtector.KeyProtectorType -eq "RecoveryPassword"} | ForEach-Object {
$bl = $_
    $bl.KeyProtector | Where-Object { $_.RecoveryPassword  } | ForEach-Object {
        $body = @{
            RowKey               = $bl.MountPoint
            PartitionKey         = $bl.ComputerName
            BitLockerRecoveryKey = $_.RecoveryPassword
            EncryptionMethod     = $bl.EncryptionMethod.ToString()
        }
        Upload-BitLockerKey -TableName "BitLocker" -RowKey $body.RowKey -PartitionKey $body.PartitionKey -entity $body
    }
}

In this example, I'm using Azure Storage Explorer to configure the Table and Access-Tokens...

1. Create Table

Start Azure Storage Explorer, logon with your AzureAD account and select the Storage-Account you want to use. If you do not have a Storage Account, create one in the Azure Portal by following the QuickStart guide.

Right click "Tables" and create a Table "BitLocker" to store the BitLocker Keys:
CreateTable

Once you have the Table, click on "Add" to add a new Entity. There are two predefined Properties: PartitionKey and RowKey. We will use the PartitionKey to store the Computername and the RowKey to store the volume as there may be multiple encrypted volumes on a Device. Additionally, add the two following properties:

  1. BitLockerRecoveryKey, to store the Recovery-Key
  2. EncryptionMethod, to store the EncryptionMethod (for reporting only)

AddColumns

2. Create Access Policy

We have to create an Access Policy (right click the BitLocker Table and select Manage Access policies…
AccessPolicy
Select the BitLocker Table and define a Name (Id) for your Access Policy. Limit the Access with an expiry time (you will have to update the PowerShell script from time to time with a new token !!) and only grant Add and Update rights to prevent that someone with the token can read all the Recovery-Keys.

3. Get Shared Access Signature

To get a SAS Token, right click the BitLocker Table and select Get Shared Access Signature…
SAS
Select your previously created Access Policy and click on Create to generate a new token.
Replace the $sasToken value in the PowerShell script with the Query string

Note: the Token must Always start with a '?'.
You must also update the $storageAccount in the PowerShell with your Azure Storage-Account.

4. Backup Recovery-Keys

As the Recovery-Key is not rotating, you do not have to backup the key on a daily base.
You can create a temporary File after the Key-Upload, so you only have to run the Backup-Script if that file is missing.
Example:
if ((test-path "$env:temp\bitlocker.tmp") -eq $false) { … #run backup and create $env:temp\bitlocker.tmp … }

The client must not be AD or Azure-AD joined as the script contains the sasToken with the required permissions to upload the key.

5. Get a Recovery Key

To get a Recovery-Key, you also need a sasToken but in this scenario, it must have Query rights.

function Get-BitLockerKey($Hostname) {
    $storageAccount = "xxx"
    $sasToken = "?st=2018-12-02T17%3A25%3A26Z&se=2018-12-03T17%3A25%3A26Z&sp=r&sv=2018-03-28&tn=bitlocker&sig=Ta%2BT%2Fqd3cYxFNOrX90aTiktYtmctcXbX6IjiguowXz4%3D"
    $version = "2017-04-17"
    $resource = "BitLocker(PartitionKey='$Hostname',RowKey='C:')$sasToken"
    $table_url = "https://$storageAccount.table.core.windows.net/$resource"
    $GMTTime = (Get-Date).ToUniversalTime().toString('R')
    $headers = @{
        'x-ms-date'    = $GMTTime
        "x-ms-version" = $version
        Accept         = "application/json;odata=fullmetadata"
    }
    (Invoke-RestMethod -Uri $table_url -Headers $headers -ContentType application/json).BitLockerRecoveryKey
}

#Example:
Get-BitLockerKey Computer01