Documenting with PowerShell: Chapter 2 – Documenting Bitlocker keys

Our RMM system currently does not have support to securely store the bitlocker key inside of the RMM system itself. I’ve subscribed to the school of bitlocking everything that passes through my company, So also computers that sometimes never get connected to Azure AD, Active Directory to store the key in. We also get users that lost the USB drive or piece of paper that the key was stored on.

As we use a documentation system (IT-Glue) to store all our passwords, I figured why not try to also store our Bitlocker keys there, while tagging the device too so we can always find which device belongs to which key easily.

First for the none IT-Glue users I’ll generate a HTML file. With some small adaptation you can upload this to Confluence, ITBoost, or any other system you use. After that example, we’ll get onto IT-Glue again. So let’s get started!

Base script

The base script is the part of the script that captures the data that we want. In our case This will be the Bitlocker key, and output it an HTML file in C:\Temp\Temp.html You can use this script however you’d like.

$BitlockVolumes = Get-BitLockerVolume
#Some HTML to make the page pretty.
$head = @"
<script>
function myFunction() {
    const filter = document.querySelector('#myInput').value.toUpperCase();
    const trs = document.querySelectorAll('table tr:not(.header)');
    trs.forEach(tr => tr.style.display = [...tr.children].find(td => td.innerHTML.toUpperCase().includes(filter)) ? '' : 'none');
  }</script>
<title>Audit Log Report</title>
<style>
body { background-color:#E5E4E2;
      font-family:Monospace;
      font-size:10pt; }
td, th { border:0px solid black; 
        border-collapse:collapse;
        white-space:pre; }
th { color:white;
    background-color:black; }
table, tr, td, th {
     padding: 2px; 
     margin: 0px;
     white-space:pre; }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px; }
h2 {
font-family:Tahoma;
color:#6D7B8D;
}
.footer 
{ color:green; 
 margin-left:10px; 
 font-family:Tahoma;
 font-size:8pt;
 font-style:italic;
}
#myInput {
  background-image: url('https://www.w3schools.com/css/searchicon.png'); /* Add a search icon to input */
  background-position: 10px 12px; /* Position the search icon */
  background-repeat: no-repeat; /* Do not repeat the icon image */
  width: 50%; /* Full-width */
  font-size: 16px; /* Increase font-size */
  padding: 12px 20px 12px 40px; /* Add some padding */
  border: 1px solid #ddd; /* Add a grey border */
  margin-bottom: 12px; /* Add some space below the input */
}
</style>
"@

foreach($BitlockVolume in $BitlockVolumes) {
$HTMLTop = @"
    <h1>Bitlocker Information</h1>
    <b>Computername: </b>$($BitlockVolume.ComputerName)<br>
    <b>Encryption Method:</b>$($BitlockVolume.EncryptionMethod)<br>
    <b>Volume Type:</b>$($BitlockVolume.VolumeType)<br>
    <b>Volume Status:</b>$($BitlockVolume.VolumeStatus)<br>
"@
$HTML += $BitlockVolume.KeyProtector | convertto-html -Head $head -PreContent "$HTMLTop <br> <h1>Keys for $($ENV:COMPUTERNAME) - $($BitlockVolume.Mountpoint)</h1>"
}
$html | Out-File C:\Temp\temp.html

Now, that’s cool. This gives us a good ol’ HTML file. We now have a choice, use the previous script found here and adapt it to upload it to IT-Glue as a Flexible Asset or make the choice to upload it as an embedded password and tag the correct device. That sounds cooler to me!

This script looks for a configuration in your IT-Glue database based on the computer’s serial number. If it finds a match it uploads the bitlocker key as an embedded password, with the name “COMPUTERNAME – DRIVE:” as an example for my computer “DESKTOP-U3984 – C:” – We do this because the hostname might change over time and you’d want the keys to be uploaded separately.

IT-Glue script

#####################################################################
$APIKEy =  "APIKEYHERE"
$APIEndpoint = "https://api.eu.itglue.com"
$orgID = "ORGIDHERE"
#####################################################################
#Grabbing ITGlue Module and installing,etc
If(Get-Module -ListAvailable -Name "ITGlueAPI") {Import-module ITGlueAPI} Else { install-module ITGlueAPI -Force; import-module ITGlueAPI}
#Settings IT-Glue logon information
Add-ITGlueBaseURI -base_uri $APIEndpoint
Add-ITGlueAPIKey $APIKEy
#This is the data we'll be sending to IT-Glue. 
$BitlockVolumes = Get-BitLockerVolume
#The script uses the following line to find the correct asset by serialnumber, match it, and connect it if found. Don't want it to tag at all? Comment it out by adding #
$TaggedResource = (Get-ITGlueConfigurations -organization_id $orgID -filter_serial_number (get-ciminstance win32_bios).serialnumber).data
foreach($BitlockVolume in $BitlockVolumes) {
$PasswordObjectName = "$($Env:COMPUTERNAME) - $($BitlockVolume.MountPoint)"
$PasswordObject = @{
    type = 'passwords'
    attributes = @{
            name = $PasswordObjectName
            password = $BitlockVolume.KeyProtector.recoverypassword[1]
            notes = "Bitlocker key for $($Env:COMPUTERNAME)"

    }
}
if($TaggedResource){ 
    $Passwordobject.attributes.Add("resource_id",$TaggedResource.Id)
    $Passwordobject.attributes.Add("resource_type","Configuration")
}

#Now we'll check if it already exists, if not. We'll create a new one.
$ExistingPasswordAsset = (Get-ITGluePasswords -filter_organization_id $orgID -filter_name $PasswordObjectName).data
#If the Asset does not exist, we edit the body to be in the form of a new asset, if not, we just upload.
if(!$ExistingPasswordAsset){
Write-Host "Creating new Bitlocker Password" -ForegroundColor yellow
$ITGNewPassword = New-ITGluePasswords -organization_id $orgID -data $PasswordObject
} else {
Write-Host "Updating Bitlocker Password" -ForegroundColor Yellow
$ITGNewPassword = Set-ITGluePasswords -id $ExistingPasswordAsset.id -data $PasswordObject
}
}

This script can also be found as an AMP file here, that’s it! as always, happy PowerShelling!

16 thoughts on “Documenting with PowerShell: Chapter 2 – Documenting Bitlocker keys

  1. Ross

    Nice post! The script works flawlessly pushing keys into ITG.
    I wonder if it would be possible to push the embedded passwords into a dedicated ‘BitLocker Keys’ folder in the password manager for easier organisation.

    Reply
      1. Brian G.

        Hi Kelvin,
        I’m also intrigued by this – how would one modify the IT Glue script to push the keys into a “Bitlocker Keys” folder?
        Thanks,
        Brian

        Reply
  2. Dan

    Hey – can you tell me if I need to place a Customer ID for each client I run this on? Or can I run it for all devices at once?

    Also – the API key, did you just create a custom API key within IT Glue (in the place near Warranty Master’s input)?

    Thank you for any help you can provide!

    Reply
  3. Pingback: Documenting with PowerShell: Passportal API Examples - CyberDrain

  4. Kay

    I’ve been getting the following errors when trying to import it the data to ITGlue. Did a bit of digging and changing the script but nothing seems to work and is driving me nuts. Any ideas?
    ——————————–
    At C:\ProgramData\CentraStage\Packages\76a97ef0-e000-4d86-8f15-ddad70dcb92c#\command.ps1:34 char:139
    + … otectorType -eq “RecoveryPassword” }).KeyProtectorId -replace ?[{}]?, …
    + ~
    You must provide a value expression following the ‘-replace’ operator.
    At C:\ProgramData\CentraStage\Packages\76a97ef0-e000-4d86-8f15-ddad70dcb92c#\command.ps1:34 char:140
    + … ctorType -eq “RecoveryPassword” }).KeyProtectorId -replace ?[{}]?, ”
    + ~~
    Unexpected token ‘?[‘ in expression or statement.
    At C:\ProgramData\CentraStage\Packages\76a97ef0-e000-4d86-8f15-ddad70dcb92c#\command.ps1:34 char:139
    + … otectorType -eq “RecoveryPassword” }).KeyProtectorId -replace ?[{}]?, …
    + ~
    The hash literal was incomplete.
    At C:\ProgramData\CentraStage\Packages\76a97ef0-e000-4d86-8f15-ddad70dcb92c#\command.ps1:39 char:5
    + }
    + ~
    Unexpected token ‘}’ in expression or statement.
    At C:\ProgramData\CentraStage\Packages\76a97ef0-e000-4d86-8f15-ddad70dcb92c#\command.ps1:56 char:1
    + }
    + ~
    Unexpected token ‘}’ in expression or statement.
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : ExpectedValueExpression
    ————————————–

    Reply
      1. Kay

        Hi Kelvin,

        Didn’t notice there was an update. This seems to work perfectly now. UTF-8 issues was not even something I had in mind when I was troubleshooting this 😀

        Reply
  5. Mark Hill

    Hi, struggling running these scripts in Datto RMM.

    If I run direct in PS works perfectly, but in DattoRMM they just time out.

    Any ideas?

    thanks
    Mark

    Reply
    1. Kelvin Tegelaar Post author

      Hi Mark,

      Did you get the latest version out of the comstore? You’ll also need to run the component “Install Nuget Provider” before you run this one.

      Reply
  6. Matthew Lewis

    two questions –

    How do I add into the script to create a BitLocker folder?

    When I run the script it creates two entries;

    Reply
  7. Chris Pegrum

    Hi,
    This is amazing, but i keep getting the following errors – any idea on how to resolve?

    Creating new Bitlocker Password
    New-ITGluePasswords : {“errors”:[{“source”:{“pointer”:”/data/attributes/password”},”detail”:”can’t be blank”,”title”:”Unprocessable Entity”,”status”:422}]}
    At line:36 char:19
    + … wPassword = New-ITGluePasswords -organization_id $orgID -data $Passwo …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,New-ITGluePasswords

    Cannot index into a null array.
    At line:17 char:1
    + $PasswordObject = @{
    + ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

    Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: ‘resource_id’ Key being added: ‘resource_id'”
    At line:27 char:5
    + $Passwordobject.attributes.Add(“resource_id”,$TaggedResource.Id)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException

    Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: ‘resource_type’ Key being added: ‘resource_type'”
    At line:28 char:5
    + $Passwordobject.attributes.Add(“resource_type”,”Configuration”)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException

    Creating new Bitlocker Password
    New-ITGluePasswords : {“errors”:[{“source”:{“pointer”:”/data/attributes/password”},”detail”:”can’t be blank”,”title”:”Unprocessable Entity”,”status”:422}]}
    At line:36 char:19
    + … wPassword = New-ITGluePasswords -organization_id $orgID -data $Passwo …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,New-ITGluePasswords

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.