Bitlocker status reporting in SCCM

Posted: November 19, 2010 in Uncategorized

I recently had to come up with a solution that will enable administrators to build reports for client’s bitlocker status. To do so i found a few articles on the web that pointed me in the right direction, however i didn’t found a complete howto for it so that’s a good reason for me to post a complete one.

To enable bitlocker status reporting in a centralised management environment with SCCM you need to follow some steps because bitlocker is not integrated that far (yet??) in the Windows OS. First, let’s first take a look at the status information that bitlocker provides:

  • Volume name, example: C: [OSDisk]
  • Disk size, example: 148,75 GB
  • BitLocker Version, example: Windows 7
  • Conversion Status, example: Fully Encrypted
  • Percentage Encrypted, example: 100%
  • Encryption Method, example: AES 128 with Diffuser
  • Protection Status, example: Protection On
  • Lock Status, example: Unlocked
  • Identification Field, example: None
  • Key Protectors (Note: multivalue), example: TPM, Numerical Password

You can check the above on clients using the commandline tool manage-bde.exe -status on Windows 7 clients. For Vista clients use cscript manage-bde.wsf -status.

Step 1. Modify and deploy SMS_DEF.MOF

We want bitlocker status information to be inventoried to SCCM. A proper way of doing that would be to add the bitlocker class to the SMS_DEF.MOF file on the management point (found in %SCCMinstallpath%\inboxes\clifiles.src\hinv). Make a copy of this file and edit with your favorite app. Add the following lines at the end of the file:

#pragma namespace (“\\\\.\\root\\cimv2\\SMS”) //make sure to use normal quotes!
#pragma deleteclass(“SCCM_BitLocker”,NOFAIL)
[ SMS_Report     (TRUE),
SMS_Group_Name (“SCCM_BitLocker”),
SMS_Class_ID   (“CUSTOM|SCCM_BitLocker|1.0”) ]
class SCCM_BitLocker : SMS_Class_Template
[SMS_Report (TRUE), key ] string Drive;
[SMS_Report (TRUE)] string DriveLabel;
[SMS_Report (TRUE)] string Size;
[SMS_Report (TRUE)] string BitLocker_Version;
[SMS_Report (TRUE)] string Conversion_Status;
[SMS_Report (TRUE)] string Percentage_Encrypted;
[SMS_Report (TRUE)] string Encryption_Method;
[SMS_Report (TRUE)] string Protection_Status;
[SMS_Report (TRUE)] string Lock_Status;
[SMS_Report (TRUE)] string Identification_Field;
[SMS_Report (TRUE)] string Key_Protectors;
[SMS_Report (TRUE)] string Automatic_Unlock;
[SMS_Report (TRUE)] string ScriptLastRan;

[edit] Make sure to replace any fancy double quotes with normal quotes after copying because otherwise compiling or parsing will fail. Or download this file: bitlocker_mof.

You should check the file for errors with mofcomp.exe -check SMS_DEF.MOF. To enable the MOF file on a single client run the following command on the client mofcomp -class:forceupdate %pathtofile%\SMS_DEF.MOF. Copy the edited file back to enable it on your ConfigMgr site.

Step 2. Install Bitlocker

Create and link a GPO to apply on the bitlocker clients that contain the following settings (or similair based on your own requirements):

Machine | Administrative Templates | System | Trusted Platform Module Services
Turn on TPM backup to Active Directory Domain Services Enabled
Machine | Administrative Templates | Windows Components | BitLocker Drive Encryption | Operating Sytem Drives
Choose how BitLocker-protected operating system drives can be recovered Enabled
Allow data recovery agent Disabled
Configure user storage of BitLocker recovery information: Require 48-digit recovery passwordAllow 256-bit recovery key
Omit recovery options from the BitLocker setup wizard Enabled
Save BitLocker recovery information to AD DS for operating system drives Enabled
Configure storage of BitLocker recovery information to AD DS: Store recovery passwords and key packages
Do not enable BitLocker until recovery information is stored to AD DS for operating system drives Disabled

Enter the BIOS on your client or use tools like the Dell client configuration utility to turn on TPM, clear the TPM and activate it. After doing this enable bitlocker encryption on the machine. You can use any method to achive this.

Step 3. Add Bitlocker status to WMI & run hw inventory

Although there are multiple ways of manipulating bitlocker through WMI you still need a script to read, update and store bitlocker status information in the WMI repository (see This is because Managed Object Format (MOF) files are not installed as part of the Windows SDK and therefore the included classes are not added to the WMI repository automatically by Windows itself.

The script (credits go to Daniel Last):

# Name : Bitlocker_Status.ps1
# Purpose : Create a WMI provider for Bitlocker to report Bitlocker Status to SCCM
# Date : 19 oktober 2010
# Version : Initial Release
# Author :
# Date : 25 oktober 2010
# Change : Created the ability to process script to check Bitlocker status for every persistent volume (removeable drives excluded)
# Changed the way how to write the key protectors column to use “,” as delimiter
# Version : 1.1
# Author :

# Create Hash Table
$Arguments = @{}

# Getting WMI Objects
$Bitstatus = @(Get-WmiObject win32_EncryptableVolume -Namespace root\cimv2\Security\MicrosoftVolumeEncryption -ErrorAction SilentlyContinue) #| where-object {$_.PersistentVolumeID -ne “”})

# Check if Bitstatus is $null
If ($Bitstatus -eq $null) {
# Bitlocker is Disabled
$Arguments.Add(“Bitlocker_Status” , “Disabled”)

# Create SCCM_Bitlocker WMI Class
Remove-WmiObject SCCM_Bitlocker -ErrorAction SilentlyContinue
$newWMIObject = New-Object System.Management.ManagementClass
$newWMIObject.Name = “SCCM_BitLocker”
$newWMIObject.Properties.add(“Bitlocker_Status”, “”)
$newWMIObject.Properties.item(“Bitlocker_Status”).Qualifiers.Add(“Key”, $true)

# Write Bitlocker Status into the WMI SCCM_Bitlocker Repository
Set-WmiInstance -Class SCCM_Bitlocker -Argument $Arguments
else {
# Create SCCM_Bitlocker WMI Class
Remove-WmiObject SCCM_Bitlocker -ErrorAction SilentlyContinue
$newWMIObject = New-Object System.Management.ManagementClass
$newWMIObject.Name = “SCCM_BitLocker”
$newWMIObject.Properties.add(“Drive”, “”)
$newWMIObject.Properties.add(“Bitlocker_Status”, “”)
$newWMIObject.Properties.add(“DriveLabel”, “”)
$newWMIObject.Properties.add(“Size”, “”)
$newWMIObject.Properties.add(“BitLocker_Version”, “”)
$newWMIObject.Properties.add(“Conversion_Status”, “”)
$newWMIObject.Properties.add(“Percentage_Encrypted”, “”)
$newWMIObject.Properties.add(“Encryption_Method”, “”)
$newWMIObject.Properties.add(“Protection_Status”, “”)
$newWMIObject.Properties.add(“Lock_Status”, “”)
$newWMIObject.Properties.add(“Identification_Field”, “”)
$newWMIObject.Properties.add(“Key_Protectors”, “”)
$newWMIObject.Properties.add(“ScriptLastRun”, “”)
$newWMIObject.Properties.item(“Drive”).Qualifiers.Add(“Key”, $true)

# For each drive in Bitdrive process drive(s)
Foreach ($Bitdrive in $Bitstatus) {
$Drive = $Bitdrive.DriveLetter

$Volume = Get-WmiObject Win32_Volume | where-object {$_.Driveletter -eq $Drive}

# Capture status values in Hash Table which don’t require name translation
$Arguments.Add(“Drive” , $Drive)
$Arguments.Add(“DriveLabel” , $Volume.Label)
$Arguments.Add(“ScriptLastRun” , (Date))
$Arguments.Add(“Percentage_Encrypted” , ($Bitdrive.GetConversionStatus()).EncryptionPercentage)

# Proccessing status values for translation
$Version = ($Bitdrive.GetVersion()).Version
$ConversionStatus = ($Bitdrive.GetConversionStatus()).ConversionStatus
$EncryptionMethod = ($Bitdrive.GetEncryptionMethod()).EncryptionMethod
$ProtectionStatus = ($Bitdrive.GetProtectionStatus()).ProtectionStatus
$LockStatus = ($Bitdrive.GetLockStatus()).LockStatus
$IdentificationField = ($Bitdrive.GetIdentificationField()).IdentificationField

for($i=1; $i -le 8; $i++) {
$VolumeKeyProtectorID = ($Bitdrive.GetKeyProtectors($i)).VolumeKeyProtectorID
If ($VolumeKeyProtectorID -ne $Null) {
Switch ($i) {
1 {$KeyProtectorIDTypes = “Trusted Platform Module (TPM)”}
2 {$KeyProtectorIDTypes += “,External key”}
3 {$KeyProtectorIDTypes += “,Numeric password”}
4 {$KeyProtectorIDTypes += “,TPM And PIN”}
5 {$KeyProtectorIDTypes += “,TPM And Startup Key”}
6 {$KeyProtectorIDTypes += “,TPM And PIN And Startup Key”}
7 {$KeyProtectorIDTypes += “,Public Key”}
8 {$KeyProtectorIDTypes += “,Passphrase”}
Default {$KeyProtectorIDTypes = “None”}
$Arguments.Add(“Key_Protectors” , $KeyProtectorIDTypes)

# Translating integer values into logical names
$Size = “{0:N2}” -f ($Volume.Capacity / 1GB) + ” GB”
$Arguments.Add(“Size” , $Size)

Switch ($Version) {
0 {$Arguments.Add(“BitLocker_Version” , “UNKNOWN”)}
1 {$Arguments.Add(“BitLocker_Version” , “VISTA”)}
2 {$Arguments.Add(“BitLocker_Version” , “Windows 7”)}

Switch ($ConversionStatus) {
0 {$Arguments.Add(“Conversion_Status” , “FULLY DECRYPTED”)}
1 {$Arguments.Add(“Conversion_Status” , “FULLY ENCRYPTED”)}
2 {$Arguments.Add(“Conversion_Status” , “ENCRYPTION IN PROGRESS”)}
3 {$Arguments.Add(“Conversion_Status” , “DECRYPTION IN PROGRESS”)}
4 {$Arguments.Add(“Conversion_Status” , “ENCRYPTION PAUSED”)}
5 {$Arguments.Add(“Conversion_Status” , “DECRYPTION PAUSED”)}

Switch ($EncryptionMethod) {
-1 {$Arguments.Add(“Encryption_Method” , “The volume has been fully or partially encrypted with an unknown algorithm and key size.”)}
0 {$Arguments.Add(“Encryption_Method” , “The volume is not encrypted.”)}
1 {$Arguments.Add(“Encryption_Method” , “AES 128 WITH DIFFUSER”)}
2 {$Arguments.Add(“Encryption_Method” , “AES 256 WITH DIFFUSER”)}
3 {$Arguments.Add(“Encryption_Method” , “AES 128”)}
4 {$Arguments.Add(“Encryption_Method” , “AES 256”)}

Switch ($ProtectionStatus) {
0 {$Arguments.Add(“Protection_Status” , “PROTECTION OFF”)}
1 {$Arguments.Add(“Protection_Status” , “PROTECTION ON”)}
2 {$Arguments.Add(“Protection_Status” , “PROTECTION UNKNOWN”)}

Switch ($LockStatus) {
0 {$Arguments.Add(“Lock_Status” , “UNLOCKED”)}
1 {$Arguments.Add(“Lock_Status” , “LOCKED”)}

# Check if Identificationfield is $null
If ($IdentificationField -eq $null) {$Arguments.Add(“Identification_Field” , “None”)} else {$Arguments.Add(“Identification_Field” , $IdentificationField)}

# Check if Bitlocker status is Enabled
If ($Bitdrive.GetConversionStatus().EncryptionPercentage -eq 100) {$Arguments.Add(“Bitlocker_Status” , “Enabled”)} else {$Arguments.Add(“Bitlocker_Status” , “Disabled”)}

# Write Bitlocker Status into the WMI SCCM_Bitlocker Repository
Set-WmiInstance -Class SCCM_Bitlocker -Argument $Arguments

# Clear Hash table

} # End foreach Bitdrive
} # End Check if Bitstatus is $null


Note that this script is written for Windows 7. 

Run the script and after that initiate a hardware inventory on the client. Note that the powershell version of the script already triggers a hardware inventory. Additionally, the powershell script need to be run with the powershell execution policy to be set to RemoteSigned. Do this using the following cmdlet: Set-ExecutionPolicy RemoteSigned

[edit] You could trigger the inventory in the vbscript by adding this line: WshShell.Run “WMIC /namespace:\\root\ccm path sms_client CALL TriggerSchedule “&Chr(34) & “{00000000-0000-0000-0000-000000000001}” & Chr(34) & ” /NOINTERACTIVE”,,true

After a while you will find 2 new tables and 1 new view in the SCCM site database:

Step 4. Create the Report

The most simple report would be a report of the table containing the bitlocker inventory data. By joining other tables/views with SQL queries the possibilities are endless, however i placed that out of scope of this howto . The following tables and views are provided for this:

  • dbo.SCCM_BitLocker_DATA
  • dbo.SCCM_BitLocker_HIST
  • dbo.v_GS_SCCM_BitLocker0

I created a report in SCCM with the following query using the SQL view:

  • select * from v_GS_SCCM_BitLocker0

And there it is, the Bitlocker report:

Tested on SCCM R2 with a Windows 7 Enterprise bitlocker client.

Feel free to comment on this post.

  1. jesus says:

    I can’t see the tables at the DB:
    * dbo.SCCM_BitLocker_DATA
    * dbo.SCCM_BitLocker_HIST
    * dbo.v_GS_SCCM_BitLocker0

    May you help me?

    • dvdruit says:

      Did you run the script on the client en trigger hw inventory after that while you already modified the SMS_DEF.mof file on the site server?


  2. Jason says:

    I am having the same issue the Databases are not showing up. i followed your instructions did not have any errors but the databases do not show up.

  3. Eli says:

    Thanks for your blog. I have updated the report/customized to:

    SELECT v_R_System.Name0 AS [Computername],
    v_GS_SCCM_BitLocker0.BitLocker_Version0 AS [Bitlocker Version], v_GS_SCCM_BitLocker0.Conversion_Status0 AS [Converstion Status], v_GS_SCCM_BitLocker0.Drive0 AS [Drive],
    v_GS_SCCM_BitLocker0.DriveLabel0 AS [DriveLabel],
    v_GS_SCCM_BitLocker0.Size0 AS [Size],
    v_GS_SCCM_BitLocker0.Encryption_Method0 AS [Encryption Method], v_GS_SCCM_BitLocker0.Identification_Field0 AS [Identification Field], v_GS_SCCM_BitLocker0.Key_Protectors0 AS [Key Protectors],
    v_GS_SCCM_BitLocker0.Lock_Status0 AS [Lock Status],
    v_GS_SCCM_BitLocker0.Percentage_Encrypted0 AS [Percentage Encrypted], v_GS_SCCM_BitLocker0.Protection_Status0 AS [Protection Status], v_GS_SCCM_BitLocker0.ScriptLastRan0 AS [Script Last Run]
    FROM v_GS_SCCM_BitLocker0 INNER JOIN v_R_System ON v_GS_SCCM_BitLocker0.ResourceID = v_R_System.ResourceID

  4. Roger says:


    Do i need to run the script to fill WMI 1 time on each client and from then on the client will update this information on its own or do i need to run it say once a day to fill the WMI information?

    It looks very nice now i can make collections on this and enable bitlocker if its suspended or something like that.

  5. Roger says:

    the powersheel script creates the sccm_bitlocker in the root/cimv2 of wmi

  6. Hi! I just wish to give you a huge thumbs up
    for the excellent information you have got here on this post.

    I will be returning to your blog for more soon.

  7. Have you ever thought about creating an e-book or guest authoring on other blogs?
    I have a blog centered on the same topics you discuss and would love to have you
    share some stories/information. I know my visitors would value your work.
    If you are even remotely interested, feel free to
    send me an e-mail.

  8. web site says:

    Hi there! This is my first comment here so I just wanted
    to give a quick shout out and say I truly enjoy reading your blog posts.
    Can you suggest any other blogs/websites/forums that go over the same topics?
    Thanks for your time!

  9. Bejesh Solomon says:

    Dear Douwe,

    the link for the scripts are not working. Can you send me the scripts?


  10. Sean says:

    The link for downlaod the VB script or powershell script are not aviliable anymore. Could you pleae provide another download llink? Thanks

  11. Jeff L. says:

    The link to download the scripts is no longer working. Are you able to re-post the scripts?

  12. David Alvarez says:

    Is there any way to get a new link for the scripts? They no longer work. Thank you!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s