Using the ConfigMgr AdminService to Retrieve BitLocker Recovery Keys and Triggering Key Rotation-A Square Dozen

Using the ConfigMgr AdminService to Retrieve BitLocker Recovery Keys and

Just lately Garth Jones accused me of realizing one thing that I knew nothing about and I used to be very offended by that. A lot so, that when Bryan Dam got here to me demanding to know the keys to BitLocker keys in ConfigMgr, I made a decision I ought to determine it out. So I did. Right here’s what I do know now:

Keying in on the Concern

When attempting to automate processes round ConfigMgr, there are Methods to do issues then there are Supported Methods to do issues. Bryan requested if I knew Supported Methods to extract a Bitlocker restoration key from the ConfigMgr database in a method that marks the important thing as disclosed and forces the shopper gadget to rotate keys. Positive appeared like Garth put him as much as this.

The Key to Success

Enter AdminService. You do not forget that man proper? It’s been some time since I’ve written something about it and I can truthfully say, it has come a LONG method. I could have to replace my information quickly. The AdminService is now used as a part of the backend that enabled Cloud Connect (previously Tenant Connect) to combine into the Microsoft Endpoint Supervisor Admin Console (all of us nonetheless simply name it Intune :-)).

Once you allow Cloud Connect, you get entry to ConfigMgr attributes and strategies which have at all times been in-console-only gadgets.

Cloud Connect Sources within the MEM Console

One in every of this stuff is the Restoration Keys blade. It lets you, yep, you guessed it, see BitLocker restoration keys in your ConfigMgr managed gadgets. Once you click on the Restoration keys (preview) blade, you will notice a listing of keys and a hyperlink on each to view the Restoration Key.

RECOVERY KEYS!
RECOVERY KEYS!

Once you click on on Present restoration key you can be notified that the important thing will probably be marked disclosed and that this can set off a key rotation on the shopper. That is precisely what we wish!

Are you sure you want to trigger key rotation?
Are you certain you need to set off key rotation?
Look at this beautiful key!
Have a look at this lovely key!

What’s nice in regards to the AdminService is that it has a verbose log file. Yow will discover the log within the set up listing of your SMS Supplier server (usually your main) AdminService.log. For those who watch the log as you navigate within the MEM portal and click on by to show the restoration key, you will notice every name again to the AdminService to retrieve the information which is then displayed within the net console. The logs appear like this:

 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Processing incoming request for useful resource [https://cm01.asd.net/AdminService/v1.0/Device?$filter=SMSID eq 'GUID:3d03b4dd-2007-47da-af02-83800df961c0'&$select=MachineId,ADSiteName,CNLastOnlineTime,CNLastOfflineTime,CNAccessMP,CurrentLogonUser,CoManaged,CA_IsCompliant,ClientVersion,Domain,IsApproved,IsVirtualMachine,LastPolicyRequest,LastMPServerName,LastActiveTime,DeviceOSBuild,CNIsOnInternet,CNIsOnline,MACAddress,SiteCode], technique: [GET], Consumer - [NT AUTHORITYSYSTEM]
Header: [ServiceNotification]=[**************]
Header: [Authorization]=[**************]
Header: [Host]=[cm01.asd.net]
Context: [RemoteIpAddress]=[fe80::9d59:e444:736c:f5e2%4]
Context: [RemotePort]=[55641]
Context: [ContentType]=[]
Context: [Accept]=[]
Acquired request from the notification channel.
Efficiently validated request from Service Connection Level.
Efficiently validated person [10d75920-6cf5-48de-955c-e856de2a39de,S-1-5-21-1909024835-672986419-4090565466-1624] from tenant [**************].
Efficiently logged on person utilizing person principal title Adam@ASD.NET.
Supplier authentication degree and exception checklist not current or expired. Retrieving from database.
Consumer ASDAdam is allowed as a result of it's validated with present authentication degree Default.
Get all cases of Gadget.
Finishing request with response code [200] motive [OK]

Processing incoming request for useful resource [https://cm01.asd.net/AdminService/v1.0/Device(16777377)/RecoveryKeys], technique: [GET], Consumer - [NT AUTHORITYSYSTEM]
Header: [ServiceNotification]=[**************]
Header: [Authorization]=[**************]
Header: [Host]=[cm01.asd.net]
Context: [RemoteIpAddress]=[fe80::9d59:e444:736c:f5e2%4]
Context: [RemotePort]=[55646]
Context: [ContentType]=[]
Context: [Accept]=[]
Acquired request from the notification channel.
Efficiently validated request from Service Connection Level.
Efficiently validated person [10d75920-6cf5-48de-955c-e856de2a39de,S-1-5-21-1909024835-672986419-4090565466-1624] from tenant [**************].
Efficiently logged on person utilizing person principal title Adam@ASD.NET.
Supplier authentication degree and exception checklist updated.
Consumer ASDAdam is allowed as a result of it's validated with present authentication degree Default.
Finishing request with response code [200] motive [OK]

Processing incoming request for useful resource [https://cm01.asd.net/AdminService/v1.0/Device(16777377)/AdminService.GetRecoveryKeyValue], technique: [POST], Consumer - [NT AUTHORITYSYSTEM]
Header: [ServiceNotification]=[**************]
Header: [Content-Length]=[56]
Header: [Content-Type]=[application/json]
Header: [Authorization]=[**************]
Header: [Expect]=[100-continue]
Header: [Host]=[cm01.asd.net]
Context: [RemoteIpAddress]=[fe80::9d59:e444:736c:f5e2%4]
Context: [RemotePort]=[62213]
Context: [ContentType]=[application/json]
Context: [Accept]=[]
Acquired request from the notification channel.
Efficiently validated request from Service Connection Level.
Efficiently validated person [10d75920-6cf5-48de-955c-e856de2a39de,S-1-5-21-1909024835-672986419-4090565466-1624] from tenant [**************].
Efficiently logged on person utilizing person principal title Adam@ASD.NET.
Supplier authentication degree and exception checklist updated.
Consumer ASDAdam is allowed as a result of it's validated with present authentication degree Default.
Get occasion of Gadget with key '16777377'
Consumer ASDAdam requests to learn worth of restoration key 5eb3baec-4e9b-49f4-9aee-90d3554aef05 on gadget 16777377 (CMCB-WS01).
Finishing request with response code [200] motive [OK]

The above log entries present 3 calls to the AdminService and though a number of the knowledge is obfuscated, we are able to piece all of it collectively.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#Utilizing a GET Technique:
#Get the Gadget:
https://cm01.asd.internet/AdminService/v1.0/Gadget?$filter=SMSID eq 'GUID:3d03b4dd-2007-47da-af02-83800df961c0'&$choose=MachineId,ADSiteName,CNLastOnlineTime,CNLastOfflineTime,CNAccessMP,CurrentLogonUser,CoManaged,CA_IsCompliant,ClientVersion,Area,IsApproved,IsVirtualMachine,LastPolicyRequest,LastMPServerName,LastActiveTime,DeviceOSBuild,CNIsOnInternet,CNIsOnline,MACAddress,SiteCode

#Base Gadget Entity
https://cm01.asd.internet/AdminService/v1.0/Gadget

#ODATA syntax to filter to search out the particular gadget utilizing SMSID and passing within the gadget GUID
?$filter=SMSID eq 'GUID:3d03b4dd-2007-47da-af02-83800df961c0'

# Choose assertion to explicitly return gadget properties. We are able to exclude this if we simply need the whole lot again.
&$choose=MachineId,ADSiteName,CNLastOnlineTime,CNLastOfflineTime,CNAccessMP,CurrentLogonUser,CoManaged,CA_IsCompliant,ClientVersion,Area,IsApproved,IsVirtualMachine,LastPolicyRequest,LastMPServerName,LastActiveTime,DeviceOSBuild,CNIsOnInternet,CNIsOnline,MACAddress,SiteCode
 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
//Response knowledge. Be aware that this returned an Array since we used $filter.
{
    "@odata.context": "https://cm01.asd.internet/AdminService/v1.0/$metadata#Gadget(MachineId,ADSiteName,CNLastOnlineTime,CNLastOfflineTime,CNAccessMP,CurrentLogonUser,CoManaged,CA_IsCompliant,ClientVersion,Area,IsApproved,IsVirtualMachine,LastPolicyRequest,LastMPServerName,LastActiveTime,DeviceOSBuild,CNIsOnInternet,CNIsOnline,MACAddress,SiteCode)",
    "worth": [
        {
        "MachineId": 16777377,
        "ADSiteName": "Houston",
        "CNLastOnlineTime": "2021-11-05T21:49:33.287Z",
        "CNLastOfflineTime": "2021-11-06T01:58:14.697Z",
        "CNAccessMP": "CM01.asd.net",
        "CurrentLogonUser": null,
        "CoManaged": 1,
        "CA_IsCompliant": 0,
        "ClientVersion": "5.00.9058.1018",
        "Domain": "ASD",
        "IsApproved": 3,
        "IsVirtualMachine": true,
        "LastPolicyRequest": "2021-11-06T00:32:02Z",
        "LastMPServerName": "CM01.ASD.NET",
        "LastActiveTime": "2021-11-06T00:32:02Z",
        "DeviceOSBuild": "10.0.19043.1288",
        "CNIsOnInternet": false,
        "CNIsOnline": false,
        "MACAddress": "00:15:5D:01:11:C3",
        "SiteCode": "PS1"
        }
    ]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#Utilizing a GET Technique:
#Get the checklist of restoration key ids for the particular gadget
https://cm01.asd.internet/AdminService/v1.0/Gadget(16777377)/RecoveryKeys

#Base Gadget Entity
https://cm01.asd.internet/AdminService/v1.0/Gadget

#Gadget's ResourceId/MachineId from the ConfigMgr database.
(16777377)

#RecoveryKeys entity to retrieve the important thing ids
/RecoveryKeys
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//Response Knowledge
{
    "@odata.context": "https://cm01.asd.internet/AdminService/v1.0/$metadata#RecoveryKey",
    "worth": [
        {
        "ItemKey": 16777377,
        "RecoveryKeyId": "5eb3baec-4e9b-49f4-9aee-90d3554aef05",
        "VolumeTypeId": 1
        }
    ]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#Utilizing a POST Technique:
#Submit knowledge to the GetRecoveryKeyValue motion/perform to return the restoration key values
https://cm01.asd.internet/AdminService/v1.0/Gadget(16777377)/AdminService.GetRecoveryKeyValue

#Base Gadget Entity
https://cm01.asd.internet/AdminService/v1.0/Gadget

#Gadget's ResourceId/MachineId from the ConfigMgr database.
(16777377)

#GetRecoveryKeyValue Motion/perform to submit a physique with the restoration id key from the earlier name.
/AdminService.GetRecoveryKeyValue

#Not proven within the logs - since it is a POST technique, we have to ship in JSON a physique. The format for the physique is discovered within the metadata XML I will present later within the publish.

{
    "RecoveryKeyId" : "5eb3baec-4e9b-49f4-9aee-90d3554aef05"
}

#Response Knowledge
{
    "worth":"161964-088066-081752-251955-663949-379379-473033-388113"
}

From a shopper machine, that is equal to operating manage-bde -protectors -get c: and retrieving the Numerical Password ID and Password as proven beneath.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
C:Windowssystem32>manage-bde -protectors -get c:
BitLocker Drive Encryption: Configuration Software model 10.0.19041
Copyright (C) 2013 Microsoft Company. All rights reserved.

Quantity C: []
All Key Protectors

    Numerical Password:
      ID: {5EB3BAEC-4E9B-49F4-9AEE-90D3554AEF05}
      Password:
        161964-088066-081752-251955-663949-379379-473033-388113

    TPM:
      ID: {0884EEB4-3DE1-46BD-A6D6-258C70714B88}
      PCR Validation Profile:
        7, 11
        (Makes use of Safe Boot for integrity validation)

The Key to Data is Understanding

As I’ve talked about in earlier posts, you’ll be able to view the metadata XML for the AdminService by utilizing the next url:

https://cm01.asd.internet/AdminService/v1.0/$metadata

The half we care about is that this (From CB 2107):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 Identify="RecoveryKey">
    
         Identify="ItemKey"/>
         Identify="RecoveryKeyId"/>
    
     Identify="ItemKey" Kind="Edm.Int32" Nullable="false"/>
     Identify="RecoveryKeyId" Kind="Edm.String" Nullable="false"/>
     Identify="VolumeTypeId" Kind="Edm.Byte" Nullable="false"/>
     Identify="Gadget" Kind="Microsoft.ConfigurationManager.ObjectLibrary.Fashions.Gadget" Nullable="false">
         Property="ItemKey" ReferencedProperty="MachineId"/>
    

Be aware of the NavigationProperty above. This means that we are able to retrieve the restoration key information FROM one other entity, on this case, from a Gadget entity. If we scroll a bit of extra we’ll see the Motion or Operate that we need to name GetRecoveryKeyValue

1
2
3
4
5
 Identify="GetRecoveryKeyValue" IsBound="true">
     Identify="bindingParameter" Kind="Microsoft.ConfigurationManager.ObjectLibrary.Fashions.Gadget"/>
     Identify="RecoveryKeyId" Kind="Edm.Guid" Nullable="false"/>
     Kind="Edm.String" Unicode="false"/>

From this snippet we are able to see that we name this motion on a Gadget entity bindingParameter and must go in a physique with parameters RecoveryKeyId. That is the bit that interprets to those traces from above:

1
2
3
4
5
https://cm01.asd.internet/AdminService/v1.0/Gadget(16777377)/AdminService.GetRecoveryKeyValue

{
    "RecoveryKeyId" : "5eb3baec-4e9b-49f4-9aee-90d3554aef05"
}

Utilizing Fiddler, you’ll be able to set off this command to check it out.

Fiddler doing some coolness!
2021-11-17_19-25-58.png

There can solely be one Grasp Key or is it Key Grasp?

For this subsequent half I used to be going to indicate you find out how to watch the shopper rotate the important thing, but it surely seems that my lab’s MBAM occasion might not be in existence, so as a substitute I’ll direct you over to the at all times thorough and informative, Niall Brady!

Have you ever Recovered from the entire Key puns but?

So after digging by all of those factor we’ve got the entire items we have to write script, or construct this into an enterprise-class third get together product so individuals can leverage it different locations…

I’ve uploaded a copy of my full instance script to my GitHub repo, however listed below are the fundamentals in PowerShell.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#The script assumes you might have the useful resource ID.
param(
    [string]$ServerName
    [int]$ResourceID
)

#Get the Gadget
$Gadget = Invoke-RestMethod -Uri "https://$($ServerName)/AdminService/v1.0/Gadget($($ResourceID))" -Technique Get -UseDefaultCredentials

#Get the restoration key IDs
$KeyIDs = Invoke-RestMethod -Uri "https://$($ServerName)/AdminService/v1.0/Gadget($($ResourceID))/RecoveryKeys" -Technique Get -UseDefaultCredentials

#Loop by the Ids and return the Restoration Keys
$KeyIDs.worth | ForEach-Object {
    $Physique = @{RecoveryKeyId = $_.RecoveryKeyId} | ConvertTo-Json
    $Keys = Invoke-RestMethod -Uri "https://$($ServerName)/AdminService/v1.0/Gadget($($ResourceID))/AdminService.GetRecoveryKeyValue" -Technique Submit -Physique $Physique -UseDefaultCredentials -ContentType "utility/json" 
    $Keys
}

#Consequence
#worth
#-----
#161964-088066-081752-251955-663949-379379-473033-388113

KEYping up is Laborious to Do

Effectively that’s just about it. Hope you realized one thing new, I certain did. Thanks Bryan and Garth for the inspiration to provide it go. Hope to see you at MMSMOA in Might 2022!

We use cookies to enable site functionality and collect data about user interactions. By clicking Accept, you agree to our use for advertising, analytics, and support.