Bitlocker SSD Vulnerability

, ,

Bitlocker SSD Vulnerability

Hey all this is a super short and easy blogpost. Recently we saw some chatter online about a bitlocker ssd vulnerability. You can read about it here. Many environments will have concerns about how you can identify these systems, and how to report on this topic for management.  At my customers site we leverage the Compliance item created by my PFE @Brandon Linton. This compliance Item is useful for inventorying bit-locker status and inventorying hard drive media type. You can read about the compliance item on his blog.

in my SQL query / SSRS I’m reporting on more than just the bitlocker issue, but also a few other security features. I kind of like to always be able to see what is encrypted vs not, what is UEFI vs BIOS, Secureboot vs not, etc.

select  distinct

       VRS.NetBios_Name0 [System Name],

                 VRS.AD_Site_Name0 AS [AD Site Name],

       CSD.Model00 AS [System Model],

       CASE OSD.Version00 

                     WHEN '6.1.7601' THEN 'Windows 7'

                     WHEN '10.0.10586' THEN '1511'

                     WHEN '10.0.14393' THEN '1607'

                     WHEN '10.0.15063' THEN '1703'

                     WHEN '10.0.16299' THEN '1709'

                     WHEN '10.0.17134' then '1803'

      END AS [Windows Version],

       Case SMS_G_System_FIRMWARE.UEFI00

              When '0' THEN 'Legacy'

              WHEN '1' THEN 'UEFI'

       END AS [UEFI Information],

       CASE SMS_G_System_FIRMWARE.SecureBoot00

              WHEN '0' THEN 'Off'

              WHEN '1' THEN 'Secure Boot'

       END AS [Secure Boot Status],

       CASE EV.ProtectionStatus00

              WHEN '0' THEN 'not encrypted'

              WHEN '1' THEN 'encrypted'

        WHEN '2' THEN 'Encrypted Requires Pin'

       END AS [Bitlocker Status],


          BLE.Bustype0, -- 

          BLE.disktype0 AS [Disk Type],


          BLE.EncryptionMethod0 AS [Encryption Method],


                    -- WHEN BLE.EncryptionMethod0 = 'None' THEN 'ENCRYPT NOW' -- did not use this b/c many systems not reported encryption method data back

                    WHEN EV.ProtectionStatus00 = '0' THEN 'Encrypt Now'

                    WHEN BLE.EncryptionMethod0 = 'Aes128' THEN 'Not Vulnerable'

                                 WHEN BLE.EncryptionMethod0 = 'Aes256Diffuser' THEN 'Not Vulnerable'

                                 WHEN BLE.EncryptionMethod0 = 'Aes128' THEN 'Not Vulnerable'

                                 WHEN BLE.EncryptionMethod0 IS NULL THEN 'Not Inventoried'

                                 WHEN BLE.EncryptionMethod0 = 'XtsAes128' THEN 'Not Vulnerable'

                    WHEN BLE.disktype0 = 'SSD' AND BLE.EncryptionMethod0 = 'HardwareEncryption' THEN 'Vulnerable'

                                 ELSE 'Not Vulnerable'

          END AS [Vulnerable],


                    WHEN dg.VirtualizationBasedSecurityS0 = '0' THEN 'VBS Not Enabled' -- 

                    WHEN dg.VirtualizationBasedSecurityS0 = '1' THEN 'VBS is Enabled, but not running'

                    WHEN dg.VirtualizationBasedSecurityS0 = '2' THEN 'VBS is enabled and running' -- cred guard running

          END AS [Credential Guard]


       v_R_System_Valid AS VRS 

       INNER JOIN Computer_System_DATA AS CSD ON CSD.MachineID = VRS.ResourceID 

       INNER JOIN Firmware_DATA AS SMS_G_System_FIRMWARE ON SMS_G_System_FIRMWARE.MachineID = VRS.ResourceID

       INNER JOIN Operating_System_DATA AS OSD ON OSD.MachineID = VRS.ResourceID  


       LEFT JOIN v_FullCollectionMembership_Valid AS VCM on VRS.ResourceID = VCM.ResourceID

          LEFT JOIN V_GS_BitLockerExtended AS BLE on BLE.ResourceID = VRS.ResourceID

          LEFT JOIN v_GS_DEVICE_GUARD AS DG on DG.ResourceID = VRS.ResourceID


       EV.DriveLetter00 = 'C:'

       -- AND CSD.Model00 not in ('VMware Virtual Platform', 'Virtual Machine', 'VMware7,1')

       AND EV.ProtectionStatus00 IS NOT NULL

          AND SMS_G_System_FIRMWARE.SecureBoot00 IS NOT NULL

          AND dg.VirtualizationBasedSecurityS0 is not null

       --AND BLE.disktype0 = 'SSD'

       --AND EV.ProtectionStatus00 = '0' -- not encrypted 

       --AND EV.ProtectionStatus00 = '1' -- encrypted

Example of Query Results:


Example of Reporting: Bitlocker SSD Vulnerability


I believe there is a CU that is coming out to remediate a few things. So this may be obsolete soon.

ALSO CHECK: Leverage Windows Analytics for Modern Ops 

Post OSD Scheduled Task

, ,

Post OSD Scheduled Task

Every organization handles OSD differently. Currently at our organization we do have some apps that have been ‘baked into the task sequence’  as an ‘Install Application’ step for a very long time and are needed on every single imaged machine. These work perfectly, install consistently and generally there are no ‘exceptions’ to a PC having the software.

This isn’t the case with all of our widely distributed applications, and rather than build the logic into the task sequence we let the collections do the work after imaging. After a period of time when the appropriate collections are updated on their various schedules these new machines happily receive their software and baselines and go on their merry way as we know all healthy SCCM clients do! In the day-to-day SCCM world this works perfectly fine. Machines are added to collections through whatever method your organization uses, whether it be Direct Membership, AD Group/OU Queries, name based queries, hardware inventory based queries and there is a general understanding that machines will pop into the collection and receive their Applications/Updates/CIs in good time.

When our techs image a machine it can be helpful for it to temporarily have some expedited policy refresh rates for a period of time to speed up those after-the-fact deployments. We had tried a few collection queries to catch these ‘new machines’ so that we could deploy some aggressive Client Settings to them, but generally there is never a perfect query. Usually you catch not just new machines but instead those risen from the dead pit of being stale, or they were rejoined to the domain, maybe the client was reinstalled or some form of in place upgrade happened.  As an alternative to the collection query I wrote up a Powershell script.

What Does It Do?

It creates a scheduled task! 

The script can be ran from a ‘Run Command Line’ or ‘Run Powershell Script’ step during OSD (Typically near/at the end) with various parameter options. You’ll have to toss it into a package to serve up to the task sequence of course. It will create a scheduled task that runs specified SCCM Client Policy requests at whatever interval you want for as long as you want. Also, the task deletes itself shortly after the duration has passed. I didn’t quite include all the of schedule types because there is a very long list. But most of the key ones are there and any others can be easily added. 

How Do I Use It?

New-ClientActionScheduledTask.ps1 -Schedule MachinePol -Interval 5

This will create a schedule task that runs every 5 minutes for 24 hours. I did say aggressive at least once up there right? The task will receive a generated name based on the requested schedules. The above would produce a task named –
SCCM Action Scheduler – [MachinePol]
Which calls a file (Start-CMClientAction.ps1)  that is generated and stored in c:\windows\temp

Both the task name and the file name can be specified as parameters to the script aptly named… -FileName and -TaskName

New-ClientActionScheduledTask.ps1 -Schedule AppEval, HardwareInv, UpdateEval, UpdateScan -Interval 30 -Duration 12
Task Sequence Example

Similar results with this execution of the line above. A task is created, but with it running every 30 minutes for a 12 hour period. The task will appear in task scheduler with a title –
SCCM Action Scheduler – [AppEval,HardwareInv,UpdateEval,UpdateScan]
Which is based on the actions provided.

Where Do I Git It?

GitHub! I intend to continue adding to this GitHub repository.

Neat Stuff:

I’ve also used a couple of bits of code you might find interesting to create the scheduled task. Maybe you’ve seen it, maybe you haven’t.


This will write-out the contents of many functions (note: function, not cmdlet). I am leveraging this to generate a .ps1 file that can be easily invoked by the task sequence.

Neat right? I wrote that function though… but what is more interesting is you can do this with some built in functions! Try ${function:Clear-Host} or ${function:Get-Verb} and you can see some of the magic behind at least some commands you’ve used. Many are compiled cmdlets and are simply not expandable like this but can be dug into in other ways.

$TaskDefinition.Settings.DeleteExpiredTaskAfter = "PT0S"

While this can be a bit odd to work with, the above piece of code allows our scheduled task to ‘delete itself’ after it expires. Specifically… zero seconds after it expires. T is just a delimiter between date and time (Eg. days vs hours/minutes/seconds). You will need to specify an ‘EndBoundary’ for this to function, which is what our ‘Duration’ is in this.

function New-ScheduledTaskTimeString {
        [Parameter(Mandatory = $false)]
        [int]$Hours = 0,
        [Parameter(Mandatory = $false)]
        [int]$Minutes = 0
    $TimeSpan = New-TimeSpan -Hours $Hours -Minutes $Minutes
    $TimeSpanDays = $TimeSpan | Select-Object -ExpandProperty Days
    $TimeSpanHours = $TimeSpan | Select-Object -ExpandProperty Hours
    $TimeSpanMinutes = $TimeSpan | Select-Object -ExpandProperty Minutes

    if ($TimeSpanDays -gt 0) {
        $OutputDays = [string]::Format("{0}D", $TimeSpanDays)

    if ($TimeSpanHours -gt 0 -or $TimeSpanMinutes -gt 0) {
        $Delimiter = 'T'
        if ($TimeSpanHours -gt 0) {
            $OutputHours = [string]::Format("{0}H", $TimeSpanHours)

        if ($TimeSpanMinutes -gt 0) {
            $OutputMinutes = [string]::Format("{0}M", $TimeSpanMinutes)

    [string]::Format("P{0}{1}{2}{3}", $OutputDays, $Delimiter, $OutputHours, $OutputMinutes)


You give me hours and minutes, I give you a PnDTnHnMnS string to use for a scheduled task.
Just a quick function I wrote for the purposes of dumping out usable strings for time intervals that task scheduler understands.

I opted to not use the *-ScheduledTask* commands available post-Win 7. You could ‘simplify’ the code a bit for the task creation if you don’t mind being incompatible with Windows 7 by using these.

Is this the right way to do it? Who knows! I’m sure with some very careful inspection and categorization of our collections and their refresh schedules we could help the situation in other ways. Still a neat bit of code.

Cody Mathis


ALSO CHECK : Keeping the SCCM Cache Clean with DCM

Collection Schedule Query

, , , , ,

SCCM Collection Schedule Queries.

There is no real readable output of the SCCM Collection schedule for us to understand. What is produced  is 16 character string. If you want to really find out what this translated to then you have to look into the SDK for translation. Also, there really are not any good existing reports/queries out there to cover this information via SQL. This is the query I came up with for my customers environment when trying to evaluate their SCCM Collection evaluation problems. Also I will do another blogpost with the sql queries to identify a number of other items to include orphaned collections, SCCM Collection  update types, etc… There is a screenshot in the bottom of this post to give a preview of the information.

Let’s show an example.

There schedule produced “29B66B4000100200” is not readable. You have to look up this information in the SDK to start to get an understanding.

The First “7” digits translate to the effective schedule date for example: Effective 6/22/2015 1:10pm
The last “9” digits translate to the actual schedule that is performed for example: Every 2 hours


SCCM Collection

The code:



CG.SITEID AS [Collection ID],

CASE VC.CollectionType

WHEN 0 THEN ‘Other’

WHEN 1 THEN ‘User’

WHEN 2 THEN ‘Device’

ELSE ‘Unknown’ END AS CollectionType,

CG.schedule, case

WHEN CG.Schedule like ‘%000102000’ THEN ‘Every 1 minute’

WHEN CG.Schedule like ‘%00010A000’ THEN ‘Every 5 mins’

WHEN CG.Schedule like ‘%000114000’ THEN ‘Every 10 mins’

WHEN CG.Schedule like ‘%00011E000’ THEN ‘Every 15 mins’

WHEN CG.Schedule like ‘%000128000’ THEN ‘Every 20 mins’

WHEN CG.Schedule like ‘%000132000’ THEN ‘Every 25 mins’

WHEN CG.Schedule like ‘%00013C000’ THEN ‘Every 30 mins’

WHEN CG.Schedule like ‘%000150000’ THEN ‘Every 40 mins’

WHEN CG.Schedule like ‘%00015A000’ THEN ‘Every 45 mins’

WHEN CG.Schedule like ‘%000100100’ THEN ‘Every 1 hour’

WHEN CG.Schedule like ‘%000100200’ THEN ‘Every 2 hours’

WHEN CG.Schedule like ‘%000100300’ THEN ‘Every 3 hours’

WHEN CG.Schedule like ‘%000100400’ THEN ‘Every 4 hours’

WHEN CG.Schedule like ‘%000100500’ THEN ‘Every 5 hours’

WHEN CG.Schedule like ‘%000100600’ THEN ‘Every 6 hours’

WHEN CG.Schedule like ‘%000100700’ THEN ‘Every 7 hours’

WHEN CG.Schedule like ‘%000100B00’ THEN ‘Every 11 Hours’

WHEN CG.Schedule like ‘%000100C00’ THEN ‘Every 12 Hours’

WHEN CG.Schedule like ‘%000101000’ THEN ‘Every 16 Hours’

WHEN CG.Schedule like ‘%000100008’ THEN ‘Every 1 days’

WHEN CG.Schedule like ‘%000100010’ THEN ‘Every 2 days’

WHEN CG.Schedule like ‘%000100028’ THEN ‘Every 5 days’

WHEN CG.Schedule like ‘%000100038’ THEN ‘Every 7 Days’

WHEN CG.Schedule like ‘%000192000’ THEN ‘1 week’

WHEN CG.Schedule like ‘%000080000’ THEN ‘Update Once’

WHEN CG.SChedule = THEN ‘Manual’

END AS [Update Schedule],

Case VC.RefreshType

when 1 then ‘Manual’

when 2 then ‘Scheduled’

when 4 then ‘Incremental’

when 6 then ‘Scheduled and Incremental’

else ‘Unknown’

end as RefreshType,



dbo.collections_g CG

left join v_collections VC on VC.SiteID = CG.SiteID

–Where CG.CollectionName like ‘%minutes’

order by

CG.Schedule DESC

The results of the query:


SCCM Collection

NOTE: I did not put a case when statement for every possible outcome, only the ones that exist in my customers environment. If you want to look up what schedule is used for a specific SCCM Collection then modify the where statement to site that specific SCCM Collection name.

Here is the SSRS report:

I plan on in the near future releasing a revised version where you can see update times over the last 7 days included.


SCCM Collection