Worklet: Install BitLocker and store keys in device tag

  • 22 September 2020
  • 36 replies
  • 1553 views

Userlevel 3
Badge

Hello everyone,


This is just a script that will store bitlocker recovery keys in the device tags for whatever device the script is run on. Thought this would be an easier way to manage any bitlocker keys for those who will want to install it through automox rather than manually exporting keys or storing them via AD.


Only thing that needs changing is the API Key. Currently there is a placeholder of just “INSERT API KEY HERE” and you must add in your own for it to work.


Remediation Code:


$apiKey = "INSERT API KEY HERE"
$apiUrl = "https://console.automox.com/api/servers/"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")
#$headers.Add("Cookie", "ax_session=eyJpdiI6IlpqM3NxdU03Mk81dHg0RjBkOGZRQmc9PSIsInZhbHVlIjoiL0Z2QVljTVF3SkV2L0Jod0lKMUFHQ0dMalMxQVNNdkswNWRRb2hkN1NiTHBFcWk4S2pXempXU2hpQzhybHE4b3loREhXRUxKSHZPMDBmMHVHNERzVlU2enphQm1sTTA3U2FCSmNlU0lJSjdEKzdDRHRqZXBlNXFSUThWb01zNk5UdXZheXpUNlExeDJKRXdONlZBbFZsV0tYc1hVMkl1Sm1QMFBvcGxKSEZYMkJNS3FkOXpPSStVV1JwNGVEN3ZlYkl5dHhvZnNHNURzR1FjMEUvZCthVyt4S09ZQmt1R2xaQUFzOGJpTmozTVcrdWtMNVdNQUR1cUNwUi9lU0lDamJVVkoydm84UEdsR2NldEVENVQ2TTdweWVkeGJtdjJaeHp6MlVpSUZXUE92MXZqbU0vSjRFcG9CWDVjZkYzeGEycnN5RGZKbDJReFIvbzdKdXRoZ0VVRnEweXhGSHd1dWozWlhTSUhNMVNVMEVGV0Faakw3ZVVWUFI5NDRSSDJKR3BhdkZ5Yk9EeklkNGtPOEhLb1B0T3pJci9JMFpDMUk5dExXZmJINHZmSDk1c3c5QlcyTWwxZEs0V1orVlNmWjFYdVRUZTg1WVNFMVhsdG9STThYMmduWEc4T1A5VGpoU2d6WVRlWXY1N3ByVkFlRVlOcExqT1hxdnpIRHN1RzMiLCJtYWMiOiIzNzc3MzM2MWM0MmQxMTA4NjliZTk2MzEwOTg1NWQ5NWNmZTZhODBlZTQyZGQwMTM3YTI3YTJhMTgwYTg5NzcwIn0%3D")

$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response | ConvertTo-Json


$currentMachineName = $env:COMPUTERNAME;

foreach($server in $response){
$name = $server.name;
if($currentMachineName -eq $name){
$id = $server.id
$organization_id = $server.organization_id
$server_group_id = $server.server_group_id
#Bitlocker Start
$keyPath = 'C:\temp'
$toEncrypt = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'Decrypted' }

# Loop through each Unencrypted Drives
# Enable Bitlocker and Export their Recovery Keys
foreach ( $drive in $toEncrypt )
{
$driveLetter = $drive.MountPoint.Replace(':','')
try {
#Enable Bitlocker
Enable-BitLocker -MountPoint $driveLetter -EncryptionMethod Aes128 -RecoveryPasswordProtector -SkipHardwareTest | Out-Null

#Export Key and Key ID to a File
$recID = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.KeyProtectorID
$recKey = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.RecoveryPassword
$keyAndID = "Recovery Key: $recKey | and Recovery ID: $recID"
#Set-Content -Path "$keyPath\BitlockerRecoveryKey_$driveLetter.txt" -Force -Value "Recovery Key ID: $recID"
#Add-Content -Path "$keyPath\BitlockerRecoveryKey_$driveLetter.txt" -Value "Recovery Key: $recKey"

#Modify device through API
## Now calling modify device API and set recID,recKey in tags
$headers.Add("Content-Type", "application/json")
$apiUrl = "https://console.automox.com/api/servers/$($id)?o=$($organization_id)"
$body = "{
`"server_group_id`": $server_group_id,
`"tags`": [
`"RecoveryID: $recID`",
`"RecoveryKey: $recKey`"
]
}"

$body
$response = Invoke-RestMethod $apiUrl -Method 'PUT' -Headers $headers -Body $body
$response | ConvertTo-Json
#endregion here is modify device API code ends
$KeyProperties = @()
$KeyObj = @()
$Computer = $env:Computername
$Keys = Get-BitlockerVolume -MountPoint C:
$selected = $Keys | Select-Object -ExpandProperty KeyProtector
$Selected[1] | select-Object KeyprotectorID, RecoveryPassword
Foreach ($S in $Selected) {
$KeyProperties = [pscustomobject]@{
Computer = $Computer
KeyProtectorID = $S.KeyProtectorID
RecoveryPassword = $S.RecoveryPassword
}
$KeyObj += $KeyProperties
}
$KeyObj[1] | Export-CSV "C:\$($Computer)_Keys.csv" -NoTypeInformation
Write-host $keyAndID
} catch {
Write-Output "Unable to Encrypt $($drive.MountPoint)"
}
}
#end of bitlocker code

}
}

36 replies

Userlevel 4
Badge

Okay this is freaking awesome! Nicely done @JHagstrom! I married my modified bitlocker remediation from our previous post with the addition you provided. This is modified in the sense that it will check for both decrypted + encrypted drives and populate them properly in the tags field. Additionally, I removed any keys being stored on the local drive and only check for the C drive to be encrypted (We have a lot of users who put in external media that cannot be encrypted). Additionally, I also nuked the RecoveryID since it provides little value to us and takes up a tag slot.

Overall, This is an amazing idea and the way you parse through the API data is sweet! Let me know what you think of my additions.


Function Invoke-AutomoxAPI{
[cmdletbinding()]
Param()

$apiKey = "YOUR API KEY"
$apiUrl = "https://console.automox.com/api/servers/"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")

#Get all devices in the org to parse
$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response | ConvertTo-Json
$currentMachineName = $env:COMPUTERNAME;
#Perform loop to figure out what our deviceID is and populate variables
foreach($server in $response){
$name = $server.name;
if($currentMachineName -eq $name){
$id = $server.id
$organization_id = $server.organization_id
$server_group_id = $server.server_group_id

#Modify device through API
## Now calling modify device API and set recKey in tags
$headers.Add("Content-Type", "application/json")
$apiUrl = "https://console.automox.com/api/servers/$($id)?o=$($organization_id)"
$body = "{
`"server_group_id`": $server_group_id,
`"tags`": [
`"RecoveryKey: $recKey`"
]
}"
$body
$response = Invoke-RestMethod $apiUrl -Method 'PUT' -Headers $headers -Body $body
$response | ConvertTo-Json
}
}
}
$toEncrypt = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'FullyDecrypted' -and $_.MountPoint -match 'c:'}
$Encrypted = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'FullyEncrypted' -and $_.MountPoint -match 'c:'}
# Enable Bitlocker if its not already and Export the Recovery Key
foreach ( $drive in $toEncrypt ) {
$driveLetter = $drive.MountPoint.Replace(':','')
try {
#Enable Bitlocker
Enable-BitLocker -MountPoint 'C:' -EncryptionMethod Aes256 -SkipHardwareTest -UsedSpaceOnly -RecoveryPasswordProtector | Out-Null
#Save Key into variable to push to AutoMox
$recKey = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.RecoveryPassword
Invoke-AutomoxAPI
} catch {
Write-Output "Unable to encrypt drive"
}
}
#Find already bitlockered volume and export the recovery key
foreach ( $drive in $Encrypted ) {
$driveLetter = $drive.MountPoint.Replace(':','')
try {
#Save Key into variable to push to AutoMox
$recKey = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.RecoveryPassword
Invoke-AutomoxAPI
} catch {
Write-Output "Unable to get already encrypted drive"
}
}
Userlevel 4
Badge

I tested both scripts but got no results, just a lot of data in the the activity log report details column. Any suggestions? Do you mean for this workflow to display the keyID and keycode here: image ?

Userlevel 4
Badge

Hey @cfrieberg, Yeah thats where we have the keys end up in the tags field. the first things I would check are this:


1: Do you have a TPM chip on your machine? Can you bitlocker manually? Can you invoke this specific command

Enable-BitLocker -MountPoint 'C:' -EncryptionMethod Aes256 -SkipHardwareTest -UsedSpaceOnly -RecoveryPasswordProtector


2: Powershell - Get-BitlockerVolume and make sure you are either “FullyDecrypted” or “FullyEncrypted”? You can change that “Where-Object” status to whatever fits your needs


3: I like to use Microsoft Visual Code as my IDE and setup breakpoints throughout the script, and run in debug mode. That way I can step through each line of code and see exactly what its doing.


Feel free to PM me if you need any more assistance and we can debug 😃

Userlevel 3
Badge

Hello @cfrieberg !


For sure try out what rmatthews suggested. A few other things to check would be:


1- Make sure you have no group policies related to Bitlocker key storage that might force the keys to be stored in AD or anything related.


2- As @Mrichards suggeted in #3 you can try running the script in a local powershell IDE and see if it outputs any specific errors to get a better idea of whats going on.

Userlevel 3
Badge

Hey @Mrichards,


Great additions! I would suggest if anyone finding this to use this above as its a great adjustment to the original one I posted.

Badge

Running in to same issues as @cfrieberg. No errors, but no results in Tags field. I just see a ton of activity both locally when running in Powershell as well as in activity log.

I can successfully run commands 1 and 2 that @Mrichards noted, no issues there.

This machine was set up to output Bitlocker key to Azure AD, but not through group policy. Why would that cause anything other than the “Unable get already encrypted drive” error to appear?


Something I’m curious about as well is that when running this worklet on a test group that only includes my machine, I receive results back about all sorts of devices in the console that are outside of the group my machine is in. Seems odd? I’m looking at the comment in Line 10 of code from @Mrichards but then I see some


Would really love to verify that this only runs against machines in group the policy is applied to instead of trying to run against every endpoint we manage.

Userlevel 4
Badge

I would suggest running this code in Visual Studio Code or some other IDE where you can setup breakpoints, and see what your variables are before the whole thing runs. Then you can step through, figure out a better idea of what is going on.


As for all the activity log data; That is intended, but I am sure theres a better way to mute it all. When we do that API call, we are calling for all our endpoints data, then doing that giant foreach loop to figure out what our deviceID is. I’ve been asking AX to find a way to hardcode a variable or some way for us to know what our ID is without querying the entire orgs data.


$response = ***call ALL our org data***
foreach($server in $response){
$name = $server.name;
if($currentMachineName -eq $name)

Its not performing any actions against other devices, we just aren’t provided a DeviceID, so we gotta go the hard way around to find it. Once we find it, we then append that ID to our API PUT command. If we didn’t have our ID, we wouldn’t have any way to tell AX where our bitlocker key goes


$apiUrl = "https://console.automox.com/api/servers/$($id)?o=$($organization_id)"

Hope this helps!

Userlevel 3
Badge

Hi @jloudon,


Generally what i’ve found is if there is no errors and no keys or encryption it means:



  • Either the drive isn’t capable of encrypting/the hardware test failed. (Generally no TPM chip).


For the comment regarding why all the organization machines show up. This uses a rather basic method and essentially pulls all the machines in your organization then will match the device to the one the script is running on. That is why the logs show every machine in the org. But, it will only run on the machines that the policy is assigned to.

Badge

Thanks to you both for your replies and more info about what is returned when running script. I can certainly test further but was hoping this would be plug and play.



I was testing this on my machine which was already encrypted with Bitlocker and uses a TPM chip. FWIW I did have success running the following script locally to pull my recovery key. I am not a PowerShell expert by any means and while this looks similar to the existing scripts, it is slightly different. I am not sure why this returns results when the other one doesn’t.


Set-ExecutionPolicy -ExecutionPolicy Bypass

# Identify all the Bitlocker volumes.
$BitlockerVolumers = Get-BitLockerVolume

# For each volume, get the RecoveryPassowrd and display it.
$BitlockerVolumers |
ForEach-Object {
$MountPoint = $_.MountPoint
$RecoveryKey = [string]($_.KeyProtector).RecoveryPassword
if ($RecoveryKey.Length -gt 5) {
Write-Output ("The drive $MountPoint has a recovery key $RecoveryKey.")
}
}
Userlevel 3
Badge

If its already encrypted that might be why its not grabbing anything. There is a different method to doing that. This script specifically encrypts, grabs keys during the encryption process and stores them.

Userlevel 4
Badge

Catching up on this thread now.


Has anyone been successful in getting keys posted to tags?


Is there a difference if the device already has encryption turned on?


Our team will continue to run our script that posts the keys in the activity log for now, unless there’s success to the tag section.

Userlevel 3
Badge

Yes, my worklet actively encrypts and posts keys to tags. The only cases where is either fails or posts no key but text is if the computer doesnt support Bitlocker: Secure boot is off, Version of Windows 10 that doesn’t have Bitlocker, or does not have a TPM.


Im not sure if @jloudon is using lenovos. But I have specifically seen it where Lenovo Thinkpad T470 & T470s will not work if secure boot if off. Has something to do with them putting a different type of TPM in that model.

Userlevel 3
Badge

Hey @cfrieberg,


Thought i’d reply. As since originally posting this I have made a few changes based on what MRichards posted and some changes of my own. As it currently stands in my environment this worklet does post recovery keys/ID in tags. Not sure if it will help your issues, but here is my current working code:


Evaluation:


$BLinfo = Get-Bitlockervolume
if($blinfo.ProtectionStatus -eq 'On' -and $blinfo.EncryptionPercentage -eq '100'){
Write-Output "'$env:computername - '$($blinfo.MountPoint)' is encrypted and compliant"
exit 0
} else {
Write-Output "'$env:computername - '$($blinfo.MountPoint)' is not encrypted and non-compliant"
exit 1
}

Remediation Code:


$apiKey = "*YOUR API KEY HERE*"
$apiUrl = "https://console.automox.com/api/servers/"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")
#$headers.Add("Cookie", "ax_session=eyJpdiI6IlpqM3NxdU03Mk81dHg0RjBkOGZRQmc9PSIsInZhbHVlIjoiL0Z2QVljTVF3SkV2L0Jod0lKMUFHQ0dMalMxQVNNdkswNWRRb2hkN1NiTHBFcWk4S2pXempXU2hpQzhybHE4b3loREhXRUxKSHZPMDBmMHVHNERzVlU2enphQm1sTTA3U2FCSmNlU0lJSjdEKzdDRHRqZXBlNXFSUThWb01zNk5UdXZheXpUNlExeDJKRXdONlZBbFZsV0tYc1hVMkl1Sm1QMFBvcGxKSEZYMkJNS3FkOXpPSStVV1JwNGVEN3ZlYkl5dHhvZnNHNURzR1FjMEUvZCthVyt4S09ZQmt1R2xaQUFzOGJpTmozTVcrdWtMNVdNQUR1cUNwUi9lU0lDamJVVkoydm84UEdsR2NldEVENVQ2TTdweWVkeGJtdjJaeHp6MlVpSUZXUE92MXZqbU0vSjRFcG9CWDVjZkYzeGEycnN5RGZKbDJReFIvbzdKdXRoZ0VVRnEweXhGSHd1dWozWlhTSUhNMVNVMEVGV0Faakw3ZVVWUFI5NDRSSDJKR3BhdkZ5Yk9EeklkNGtPOEhLb1B0T3pJci9JMFpDMUk5dExXZmJINHZmSDk1c3c5QlcyTWwxZEs0V1orVlNmWjFYdVRUZTg1WVNFMVhsdG9STThYMmduWEc4T1A5VGpoU2d6WVRlWXY1N3ByVkFlRVlOcExqT1hxdnpIRHN1RzMiLCJtYWMiOiIzNzc3MzM2MWM0MmQxMTA4NjliZTk2MzEwOTg1NWQ5NWNmZTZhODBlZTQyZGQwMTM3YTI3YTJhMTgwYTg5NzcwIn0%3D")

$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response | ConvertTo-Json


$currentMachineName = $env:COMPUTERNAME;

foreach($server in $response){
$name = $server.name;
if($currentMachineName -eq $name){
$id = $server.id
$organization_id = $server.organization_id
$server_group_id = $server.server_group_id
#region your bitlocker code start
$keyPath = 'C:\temp'
$toEncrypt = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'Decrypted' }

# Loop through each Unencrypted Drives
# Enable Bitlocker and Export their Recovery Keys
foreach ( $drive in $toEncrypt )
{
$driveLetter = $drive.MountPoint.Replace(':','')
try {
#Enable Bitlocker
Enable-BitLocker -MountPoint $driveLetter -EncryptionMethod Aes256 -RecoveryPasswordProtector | Out-Null

#Export Key and Key ID to a File
$recID = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.KeyProtectorID
$recKey = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.RecoveryPassword
$keyAndID = "Recovery Key: $recKey | and Recovery ID: $recID"
#Set-Content -Path "$keyPath\BitlockerRecoveryKey_$driveLetter.txt" -Force -Value "Recovery Key ID: $recID"
#Add-Content -Path "$keyPath\BitlockerRecoveryKey_$driveLetter.txt" -Value "Recovery Key: $recKey"

#region here is modify device API code starts
## Now calling modify device API and set recID,recKey in tags
$headers.Add("Content-Type", "application/json")
$apiUrl = "https://console.automox.com/api/servers/$($id)?o=$($organization_id)"
$body = "{
`"server_group_id`": $server_group_id,
`"tags`": [
`"RecoveryID: $recID`",
`"RecoveryKey: $recKey`"
]
}"

$body
$response = Invoke-RestMethod $apiUrl -Method 'PUT' -Headers $headers -Body $body
$response | ConvertTo-Json
#endregion here is modify device API code ends
$KeyProperties = @()
$KeyObj = @()
$Computer = $env:Computername
$Keys = Get-BitlockerVolume -MountPoint C:
$selected = $Keys | Select-Object -ExpandProperty KeyProtector
$Selected[1] | select-Object KeyprotectorID, RecoveryPassword
Foreach ($S in $Selected) {
$KeyProperties = [pscustomobject]@{
Computer = $Computer
KeyProtectorID = $S.KeyProtectorID
RecoveryPassword = $S.RecoveryPassword
}
$KeyObj += $KeyProperties
}
$KeyObj[1] | Export-CSV "C:\$($Computer)_Keys.csv" -NoTypeInformation
Write-host $keyAndID
} catch {
Write-Output "Unable to Encrypt $($drive.MountPoint)"
}
}
#endregion your bitlocker code end

}
}
Userlevel 4
Badge

Finally got around to making my own version of what you did for the evaluation part. This is a little longwinded and probably can shorten it up a bit. However, it does some cool things here, we never want one of our users to get locked out, so if we don’t have the key - we are in hot water. So compliance is only when the recovery key is in the tags section. Oh also, all of my scripts from now on should contain “deviceID” txt file hidden in amagent folder for API query speediness (So we don’t have to always do the giant foreach api loop). Let me know what you think~


#######
#User defined Variables:
$filelocation = "C:\ProgramData\amagent\deviceID.txt"
$apikey = "API KEY"
#######
Function Get-AutomoxDeviceID{
[cmdletbinding()]
Param()
$apiUrl = "https://console.automox.com/api/servers"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")
$response = (Invoke-WebRequest -Method Get -Uri $apiurl -Headers $headers).Content
$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response | ConvertTo-Json
$currentMachineName = $env:COMPUTERNAME;
foreach($server in $response){
$name = $server.name;
if($currentMachineName -eq $name)
{
$server.id | Out-File -FilePath "$filelocation" -NoClobber
echo "deviceID successfully found, writing to disk"
}
}
}
Function Invoke-CheckBitlocker{
[cmdletbinding()]
Param()
$apiUrl = "https://console.automox.com/api/servers/$deviceID"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")
$global:response = (Invoke-WebRequest -Method Get -Uri $apiurl -Headers $headers).Content
Write-Output "checking tags to see if recovery key exists"
$global:response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$global:response | ConvertTo-Json > out-null
}
#Check to see if deviceID.txt exists:
if (Test-Path -Path $filelocation)
{
$deviceID = Get-Content "$filelocation" -Raw
echo "DeviceID.txt found, importing data"
}
#######
#DeviceID doesn't exist, API call time:
else {
echo "building servers list for parsing, please wait"
Get-AutomoxDeviceID
}
Invoke-CheckBitlocker
if ($response.tags -match 'Recovery' -And $Encrypted.VolumeStatus -eq "FullyEncrypted")
{
exit 0
}
else {exit 1}

EDIT: Fixed some function scoping issues preventing false non-compliance checks

Userlevel 2

Hi there,

Has anyone tested this recently. I love the idea, but there’s nothing showing up in the tags section.

I tested it on my own laptop and it’s definitely enforcing encryption. Protectors are showing up as TPM and Recovery Key/Pass. So that’s all good. However it’s not posting to the Tags field.


Have there been any UI changes in the Console lately that might be interfering with the post?


Cheers,

Userlevel 4
Badge

Hey Dolan,


I just tested this in my environment and works for me. You are saying that you are debugging the code and can see the output of the bitlocker key? Do you have more than 500 endpoints? You might need to add pagination to the script to iterate through all of your endpoints. Basically we pull all endpoints down with the API call, and then foreach through each entry to try and match the workstation with its respective deviceID. So it sounds like it cant find your workstation with that mass API call. Which would make sense with the pagination changes, and if your workstation isn’t on the first 500 endpoints

Userlevel 2

Heya Mrichards,

I’m testing this in a separate group against 2 machines in the Infrastructure dept. before pushing company wide. Automox reports are showing the entire message including the bitlocker key, yet I’m seeing nothing in the tags sections.


Thanks for your help with this.

Userlevel 1

What happens if I run this against a computer that is already encrpyted with Bitlocker?

Userlevel 3
Badge

I haven’t tested this specific scenario in a while, but I believe it will just report that it was unable to encrypt and not show the keys or just show no keys and the API call information.


You would want to set up the evaluation code properly so it can check to verify if a machine is already encrypted before executing the Remediation code.


A couple examples of evaluation code from above:


This one is what I have set up, but is very simplistic:


$BLinfo = Get-Bitlockervolume
if($blinfo.ProtectionStatus -eq 'On' -and $blinfo.EncryptionPercentage -eq '100'){
Write-Output "'$env:computername - '$($blinfo.MountPoint)' is encrypted and compliant"
exit 0
} else {
Write-Output "'$env:computername - '$($blinfo.MountPoint)' is not encrypted and non-compliant"
exit 1
}

There is also one that @Mrichards set it which is much more detailed and does extra checks to make sure a user never gets locked out:


#######
#User defined Variables:
$filelocation = "C:\ProgramData\amagent\deviceID.txt"
$apikey = "API KEY"
#######
Function Get-AutomoxDeviceID{
[cmdletbinding()]
Param()
$apiUrl = "https://console.automox.com/api/servers"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")
$response = (Invoke-WebRequest -Method Get -Uri $apiurl -Headers $headers).Content
$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response | ConvertTo-Json
$currentMachineName = $env:COMPUTERNAME;
foreach($server in $response){
$name = $server.name;
if($currentMachineName -eq $name)
{
$server.id | Out-File -FilePath "$filelocation" -NoClobber
echo "deviceID successfully found, writing to disk"
}
}
}
Function Invoke-CheckBitlocker{
[cmdletbinding()]
Param()
$apiUrl = "https://console.automox.com/api/servers/$deviceID"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")
$response = (Invoke-WebRequest -Method Get -Uri $apiurl -Headers $headers).Content
Write-Output "checking tags to see if recovery key exists"
$Encrypted = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'FullyEncrypted' -and $_.MountPoint -match 'c:'}
$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response | ConvertTo-Json > out-null
}
#Check to see if deviceID.txt exists:
if (Test-Path -Path $filelocation)
{
$deviceID = Get-Content "$filelocation" -Raw
echo "DeviceID.txt found, importing data"
}
#######
#DeviceID doesn't exist, API call time:
else {
echo "building servers list for parsing, please wait"
Get-AutomoxDeviceID
}
Invoke-CheckBitlocker
if ($response.tags -match 'Recovery' -And $Encrypted.VolumeStatus -eq "FullyEncrypted")
{
exit 0
}
else {exit 1}
Userlevel 3
Badge

For Anyone still interested in this script I have made a lot of revisions to make the output logs much more clean with no API Junk. Added in checking for enabling Auto-Unlock on secondary drives as this was a common complaint from my users.


In addition, I have done some troubleshooting so I can hopefully provide some input on why some people arent seeing anything in logs.


New Remediation Code:


$apiKey = "*YOUR ORGS API KEY*"
$apiUrl = "https://console.automox.com/api/servers/"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")

$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response = ConvertTo-Json

$servers = $response | Sort-Object name

$currentMachineName = $env:COMPUTERNAME;

foreach($server in $servers){
$name = $server.name;
if($currentMachineName -eq $name){
$id = $server.id
$organization_id = $server.organization_id
$server_group_id = $server.server_group_id
#region your bitlocker code start
$keyPath = 'C:\temp'
$toEncrypt = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'Decrypted' }

# Loop through each Unencrypted Drives
# Enable Bitlocker and Export their Recovery Keys
foreach ( $drive in $toEncrypt )
{
$driveLetter = $drive.MountPoint.Replace(':','')
try {
#Enable Bitlocker
Enable-BitLocker -MountPoint $driveLetter -EncryptionMethod Aes256 -RecoveryPasswordProtector -SkipHardwareTest | Out-Null
Start-Sleep -s 15

#Export Key and Key ID to a File
$recID = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.KeyProtectorID
$recKey = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.RecoveryPassword
$keyAndID = "Recovery Key: $recKey | and Recovery ID: $recID"

#Start of API Modify
## Now calling modify device API and set recID,recKey in tags
$headers.Add("Content-Type", "application/json")
$apiUrl = "https://console.automox.com/api/servers/$($id)?o=$($organization_id)"
$body = "{
`"server_group_id`": $server_group_id,
`"tags`": [
`"RecoveryID: $recID`",
`"RecoveryKey: $recKey`"
]
}"

$response = Invoke-RestMethod $apiUrl -Method 'PUT' -Headers $headers -Body $body
$response | ConvertTo-Json
#End of API Modify
$KeyProperties = @()
$KeyObj = @()
$Computer = $env:Computername
$Keys = Get-BitlockerVolume -MountPoint C:
$selected = $Keys | Select-Object -ExpandProperty KeyProtector
$Selected[1] | select-Object KeyprotectorID, RecoveryPassword
Foreach ($S in $Selected) {
$KeyProperties = [pscustomobject]@{
Computer = $Computer
KeyProtectorID = $S.KeyProtectorID
RecoveryPassword = $S.RecoveryPassword
}
$KeyObj += $KeyProperties
}
$KeyObj[1] | Export-CSV "C:\$($Computer)_Keys.csv" -NoTypeInformation
Write-host $Keys
} catch {
Write-Output "Unable to Encrypt $($drive.MountPoint)"
}
}

Start-Sleep -s 60

foreach ($drive in $toEncrypt)
{
$driveLetter = $drive.MountPoint.Replace(':','')
Enable-BitLockerAutoUnlock -MountPoint $driveLetter | Out-Null
}
#End of Bitlocker Code

}
}

Some issues I’ve discovered while troubleshooting BitLocker in my org:



  • Users must have a compatible version of windows to work with Bitlocker. (Windows Pro, etc)

  • GPO Settings related to enforcing storing BitLocker keys in AD DS will prevent the install from working.

  • Check TPM Compatibility. As TPM 2.0 does not work with a GPT(UEFI) partition, BitLocker won’t be able to initialize if the tpm is not compatible. I believe most dTPMs down aday can downgrade to 1.2 if needed.

  • Make sure the keys are able to save during the process. I have noticed behavior where if the key cannot save locally, to a file server, or anywhere the BitLocker install will get stuck at “Awaiting Activation”.

Userlevel 3
Badge

@vukko Saw you reply in the other thread. Might be helpful to you.

Thank you, much appreciated!

Badge

Per your evaluation code, it looks as though if the drive is already encrypted it will exit and just write output to the activity log. Most of the drives in my organization are already encrypted; how do i get it to write the Key to the tag if this is the case?

Userlevel 3
Badge

I wasn’t able to test this on my own org as I don’t wanna mess with any tags I have set up. But in theory it would look something like this. Essentially removing the encryption part and targeting drives that are encrypted, using the “Get-BitLockerVolume” Powershell function will grab any keys from the machine.


$apiKey = "*YOUR ORGS API KEY*"
$apiUrl = "https://console.automox.com/api/servers/"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $apiKey")

$response = Invoke-RestMethod $apiUrl -Method 'GET' -Headers $headers -Body $body
$response = ConvertTo-Json

$servers = $response | Sort-Object name

$currentMachineName = $env:COMPUTERNAME;

foreach($server in $servers){
$name = $server.name;
if($currentMachineName -eq $name){
$id = $server.id
$organization_id = $server.organization_id
$server_group_id = $server.server_group_id
#region your bitlocker code start
$keyPath = 'C:\temp'
$EncryptedDrives = Get-BitLockerVolume | Where-Object { $_.VolumeStatus -match 'Encrypted' }

# Loop through each Unencrypted Drives
# Enable Bitlocker and Export their Recovery Keys
foreach ( $drive in $EncryptedDrives )
{
$driveLetter = $drive.MountPoint.Replace(':','')
try {

#Export Key and Key ID to a File
$recID = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.KeyProtectorID
$recKey = (Get-BitLockerVolume -MountPoint $driveLetter).KeyProtector.RecoveryPassword
$keyAndID = "Recovery Key: $recKey | and Recovery ID: $recID"

#Start of API Modify
## Now calling modify device API and set recID,recKey in tags
$headers.Add("Content-Type", "application/json")
$apiUrl = "https://console.automox.com/api/servers/$($id)?o=$($organization_id)"
$body = "{
`"server_group_id`": $server_group_id,
`"tags`": [
`"RecoveryID: $recID`",
`"RecoveryKey: $recKey`"
]
}"

$response = Invoke-RestMethod $apiUrl -Method 'PUT' -Headers $headers -Body $body
$response | ConvertTo-Json
#End of API Modify
$KeyProperties = @()
$KeyObj = @()
$Computer = $env:Computername
$Keys = Get-BitlockerVolume -MountPoint $driveLetter
$selected = $Keys | Select-Object -ExpandProperty KeyProtector
$Selected[1] | select-Object KeyprotectorID, RecoveryPassword
Write-host $Keys
} catch {
Write-Output "Unable to get keys for $($drive.MountPoint)."
}
}

}
}
Badge

Do I just put that code in both the Evaluation & Remediation?

Reply