If you have BitLocker keys backed up to Azure Active Directory from your Azure AD joined computers, you’ve probably found yourself looking for a way to retrieve those keys using something other than the Azure portal. Of course users can retrieve the key themselves, but there are plenty of scenario’s imaginable where you’d want a support agent to be able to look up a user’s BitLocker key for them.
In Active Directory you can accomplish this by fetching the msFVE-RecoveryInformation
objects associated with your AD computers, but there’s no comparable method for Azure AD (yet?). Get-AzureADDevice
and Get-AzureADObjectByObjectId
don’t expose nearly as much information about a device as Get-ADComputer
and Get-ADObject
!
Cue the “hidden” Azure portal API! I found out about this through a colleague’s blog post at Liebensraum. It enables you to perform various functions in Azure that you normally wouldn’t be able to using PowerShell.
Note: please be careful using this for production workflows as this is NOT supported by Microsoft.
I’ve written a function named Get-AzureADBitLockerKeysForUser
which grabs all BitLocker recovery keys from Azure AD for a certain user.
Let’s walk through it step by step!
1. Prerequisites
You’ll need two modules installed for this: AzureAD
(or AzureADPreview
) and AzureRM
, so go ahead and install those if you haven’t already.
You also need to be assigned one of the following Azure AD roles to be able to view BitLocker keys:
- Global Administrator
- Helpdesk Administrator
- Security Administrator
- Security Reader
- Intune Service Administrator
- Cloud Device Administrator
2. Connect to Azure AD and Azure RM
The function starts by connecting to both Azure AD and Azure RM, optionally using the supplied credential.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
function Get-AzureADBitLockerKeysForUser { Param ( [parameter(Mandatory = $true)] [string]$SearchString, [pscredential]$Credential ) Import-Module AzureRM.Profile if (Get-Module -Name "AzureADPreview" -ListAvailable) { Import-Module AzureADPreview } elseif (Get-Module -Name "AzureAD" -ListAvailable) { Import-Module AzureAD } if ($Credential) { Try { Connect-AzureAD -Credential $Credential -ErrorAction Stop | Out-Null } Catch { Write-Warning "Couldn't connect to Azure AD non-interactively, trying interactively." Connect-AzureAD -TenantId $(($Credential.UserName.Split("@"))[1]) -ErrorAction Stop | Out-Null } Try { Login-AzureRmAccount -Credential $Credential -ErrorAction Stop | Out-Null } Catch { Write-Warning "Couldn't connect to Azure RM non-interactively, trying interactively." Login-AzureRmAccount -TenantId $(($Credential.UserName.Split("@"))[1]) -ErrorAction Stop | Out-Null } } else { Connect-AzureAD -ErrorAction Stop | Out-Null Login-AzureRmAccount -ErrorAction Stop | Out-Null } |
3. Get Access token
It then uses Jos Lieben’s method to retrieve an OAuth token for the main.iam.ad.ext.azure.com
endpoint, and creates the header to use in the API calls:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$context = Get-AzureRmContext $tenantId = $context.Tenant.Id $refreshToken = @($context.TokenCache.ReadItems() | Where-Object {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken $body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6" $apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded' $header = @{ 'Authorization' = 'Bearer ' + $apiToken.access_token 'X-Requested-With' = 'XMLHttpRequest' 'x-ms-client-request-id' = [guid]::NewGuid() 'x-ms-correlation-id' = [guid]::NewGuid() } |
4. Find devices
Given the supplied user’s name or UserPrincipalName, it looks up all their Azure AD joined/registered devices:
1 2 3 |
$userDevices = Get-AzureADUser -SearchString $SearchString | Get-AzureADUserRegisteredDevice -All:$true |
5. Retrieve BitLocker keys
Finally, the script uses the API to retrieve the device records for the user’s devices and retrieve the available BitLocker key ID’s & recovery keys, along with the device name and drive type:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$bitLockerKeys = @() foreach ($device in $userDevices) { $url = "https://main.iam.ad.ext.azure.com/api/Device/$($device.objectId)" $deviceRecord = Invoke-RestMethod -Uri $url -Headers $header -Method Get if ($deviceRecord.bitlockerKey.count -ge 1) { $bitLockerKeys += [PSCustomObject]@{ Device = $deviceRecord.displayName DriveType = $deviceRecord.bitLockerKey.driveType KeyId = $deviceRecord.bitlockerKey.keyIdentifier RecoveryKey = $deviceRecord.bitlockerKey.recoveryKey } } } |
6. Output
The output of the function is an array of PSCustomObjects that you can use for further processing.
1 2 3 4 |
$bitLockerKeys } |
To wrap things up, here’s a screenshot of a sample run of the script:
Head over to my GitHub to grab a copy of the script, and let me know if you found it useful (or not)!