Windows Autopilot Behind The Scenes Secrets – Admin Side – Part 2

In my previous post (Autopilot FAQs), I tried to clarify the general misconceptions and give a clear overview of Windows Autopilot.

This post is a part of a series and continuation where I want to talk about Windows Autopilot Behind the Scenes from the Admin end configuration perspective. Let’s get started.

Related Posts

Extracting the Hardware Hash from the end device

It all starts with extracting the Device hash from a device using the Get-WindowsAutopilotInfo script available on GitHub.

Patch My PC

The CSV file generated by running this script is uploaded in Intune (or MSfB) to register the Device to Autopilot services.

Subscribe to this Blog via Email

[jetpack_subscription_form show_only_email_and_button=”true” custom_background_button_color=”#fcb900″ custom_text_button_color=”#eeeeee” submit_button_text=”Subscribe” submit_button_classes=”wp-block-button__link has-text-color has-very-light-gray-color has-background has-luminous-vivid-amber-background-button-color” show_subscribers_total=”true” ]

What Information does Autopilot CSV Contain?

If you check the CSV contents, the thing that will catch your attention is the extremely large HEX value. To see the contents of this HEX value, you would need to decode it. This can be done using the oa3tool.exe, part of the Windows ADK.

Related PostWindows Autopilot FAQ Clarifying the General Misconceptions Part 1

Adaptiva
oa3tool.exe - part of Windows ADK -  Windows Autopilot Behind the Scenes
oa3tool.exe – part of Windows ADK – Windows Autopilot Behind the Scenes 1

How to Decode Autopilot CSV?

As I mentioned above, you can decode the Autopilot CSV file using oa3tool.exe. The decrypted hardware hash looks similar to the below XML representation.

<?xml version="1.0"?>
 <HardwareReport>
         <HardwareInventory>
                 <p n="ToolVersion" v="3" />
                 <p n="HardwareInventoryVersion" v="131" />
                 <p n="ToolBuild" v="10.0.17763.1" />
                 <p n="OSType" v="FullOS" />
                 <p n="OsCpuArchitecture" v="x64" />
                 <p n="OsBuild" v="10.0.17763.1" />
                 <p n="OsSystemTime" v="2019-03-05T12:25:49Z" />
                 <p n="OsLocalTime" v="2019-03-06T18:25:49+30:00" />
                 <p n="ProcessorModel" v="Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz" />
                 <p n="ProcessorPackages" v="1" />
                 <p n="ProcessorThreads" v="1" />
                 <p n="ProcessorCores" v="1" />
                 <p n="ProcessorHyperThreading" v="false" />
                 <p n="SmbiosRamArrayCount" v="1" />
                 <p n="SmbiosRamSlots" v="1" />
                 <p n="SmbiosRamErrorCorrection" v="None" />
                 <p n="SmbiosRamMaximumCapacity" v="2048" />
                 <p n="TotalPhysicalRAM" v="2" />
                 <p n="SmbiosFirmwareVendor" v="Lenovo" />
                 <p n="SmbiosSystemManufacturer" v="Lenovo" />
                 <p n="SmbiosSystemProductName" v="Lenovo" />
                 <p n="SmbiosSystemSerialNumber" v="PC0QX40X" />
                 <p n="SmbiosUuid" v="1b0b2241-6194-4d64-9bab-173000ed6541" />
                 <p n="SmbiosSkuNumber" v="None" />
                 <p n="SmbiosSystemFamily" v="" />
                 <p n="SmbiosSystemVersion" v="" />
                 <p n="SmbiosBoardManufacturer" v="Intel" />
                 <p n="SmbiosBoardProduct" v="" />
                 <p n="SmbiosBoardVersion" v="" />
                 <p n="ChassisType" v="0x0#" />
                 <p n="InternalDiskCount" v="1" />
                 <p n="DeviceNumber" v="0" />
                 <p n="PrimaryDiskTotalCapacity" v="42" />
                 <p n="PrimaryDiskType" v="HDD" />
                 <p n="DiskSerialNumber" v=" 2b0b2241-6194-4d64-9bab-173000ed6541 " />
                 <p n="OpticalDiskDriveType" v="true" />
                 <p n="PhysicalMedium" v="Unspecified" />
                 <p n="MacAddress" v="00:15:5D:BA:AE:00" />
                 <p n="DisplayResolutionHorizontal" v="1024" />
                 <p n="DisplayResolutionVertical" v="768" />
                 <p n="DisplaySizePhysicalH" v="0" />
                 <p n="DisplaySizePhysicalY " v="0" />
                 <p n="DigitizerSupportID" v="IntegratedTouch, IntegratedPen, MultiInput" />
                 <p n="PowerPlatformRole" v="Mobile" />
                 <p n="TPMVersion" v="TPM-Version:2.0 -Level:0-Revision:1.16-VendorID:'MSFT'-Firmware:538247443.1394722" />
                 <p n="TPM EkPub" v="s7+hJsgnlFQ+Jf4O7WZEh9AcZcJ9EXIBGeUSkzRXDkrSt2UBJ0P1FmA8V8PTp/TbY3dmn5IG1Z2spHlrGmu1AshGHlZyIMFPUeMN91/+mM3lqWsHOrOMHjGvZrdMCJxi3sXAqs16bo5BFoNWXHXZyCwWQ3204chGlOzm309hKV+l90t7ciqzfpaA2D7UcyYy8xHm0qbuI1pNaHYkP5mmdyKn5eoHtpNTY0zjVf+ZtZIJ6N2/VydcZ5olmSG2BRe5xxZhbYILkprzyit5ayPXmUlTYm5MV6zbuZYMeU0hu4HetDAL6G0XZQz+UH/ufuvEBCe44Q/uz2UdXlgQ0cfpTQ==" />
                 <p n="Status" v="0x00000000" />
                 <p n="OfflineDeviceIdType" v="TPM_EK" />
                 <p n="OfflineDeviceId" v="2Q5jpb/RDYGngUglOzNZHh0bcv+oK08MpoOAFRtTYr8=" />
                 <p n="DiskSSNKernel" v=" " />
         </HardwareInventory>
 </HardwareReport> 

All this information is used to register the Device to the Windows Autopilot service (also known as Device Directory Service – DDS – internally).

NOTE: This is the exact info collected during the normal provisioning process. Replacing the system board changes the hardware hash for that particular Device. A device, post motherboard replacement, is subjected to new enrollment even if device records are present in Azure AD and Intune. The reason being WPJ (Work Place Join) re-evaluation as performed by Azure DRS fails for the fact that the device info stored in the Azure AD device object does not match with the present Device.

Get-WindowsAutopilotInfo Script

More information: This is the rough functioning of the Get-WindowsAutopilotInfo script.

$hh=(Get-CimInstance -ClassName MDM_DevDetail_Ext01 -Namespace root\cimv2\mdm\dmmap).DeviceHardwareData
 $serialnumber=(Get-WmiObject -Class win32_bios ).serialnumber
 $mm=(Get-WmiObject -class win32_computersystem)
 $details= New-Object psobject -Property @{
 Hardwarehash=$hh
 SerialNumber=$serialnumber
 Manufacturer=$mm.Manufacturer
 Model=$mm.Model
 }
 $details | Out-GridView 

Let’s move to the next part as the hardware hash is collected.

Autopilot Device Registration PartWindows Autopilot Behind the Scenes

This is where an IT Admin will start registering the Device to the Windows Autopilot service by uploading the Device hash to Intune.

NOTE 1 – At this point, I need to clarify that the Intune service and the Autopilot service are mostly not the same. This is a misconception I hear from many admins to date. Windows Autopilot is an entirely different independent service separate from the Intune service. 

NOTE 2Intune provides the UI which allows Admins to register devices as Autopilot devices. The same is also possible from Microsoft Store for Business (MSfB). Thus, Intune and MSfB provide a mechanism to report devices to the Windows Autopilot service, but they are not the Autopilot service in itself.

Add Windows Autopilot Devices

To understand this better, let’s look into what happens in the back end (Windows Autopilot Behind the Scenes) when an Admin uploads a device hash into Intune.

Windows Autopilot - upload device hash - Windows Autopilot Behind the Scenes
Windows Autopilot – upload device hash – Windows Autopilot Behind the Scenes 2

Graph Query

The graph query that gets generated in the backend is:

RequestURL:https://graph.microsoft.com/beta/deviceManagement/importedWindowsAutopilotDeviceIdentities/import  
HTTP Version:HTTP/1.1
Request method: POST
Type:
application/json
Raw Content:
{"importedWindowsAutopilotDeviceIdentities": [{
"serialNumber": "#######",
"productKey": "",
"hardwareIdentifier": "#######"
}]}  

Post call to Register to Windows Autopilot Service

This is the POST call made by Intune to register the Device to the Windows Autopilot service (also known as DDS – Device Directory Service). The response to this POST call is:

{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#Collection(importedWindowsAutopilotDeviceIdentity)",
"value": [{
"@odata.type": "#microsoft.graph.importedWindowsAutopilotDeviceIdentity",
"id": "4419d710-489d-4d36-bb1f-a3d0a4941727",
"orderIdentifier": "",
"groupTag": "",
"serialNumber": "8645-5788-4128-6899-2408-1523-89",
"productKey": "",
"importId": "adaf2068-abf1-4309-906a-c9b4ef250670",
"hardwareIdentifier": "",
"assignedUserPrincipalName": "",
"state": {
"deviceImportStatus": "unknown/pending/partial/complete/error",
"deviceRegistrationId": "",
"deviceErrorCode": 0,
"deviceErrorName": ""
}}]} 

Get Call to Query Import Status

The GUID, as referred to by “id,” is the object identifier in the importedAutopilotDeviceIdentity collection namespace but what is more important is the “imported” GUID with which Intune creates a GET call to query the import status.

 RequestURL: https://graph.microsoft.com/beta/deviceManagement/importedWindowsAutopilotDeviceIdentities 
 Query arguments:
 $filter ImportId eq 'adaf2068-abf1-4309-906a-c9b4ef250670'
 $select id,state
 HTTP Version: HTTP/1.1
 Request method: GE
 GET Response Content:
 {
 "@odata.context": "https://graph.microsoft.com/beta/$metadata#deviceManagement/importedWindowsAutopilotDeviceIdentities", "value": [{
 "@odata.type": "#microsoft.graph.importedWindowsAutopilotDeviceIdentity",
 "id": "4419d710-489d-4d36-bb1f-a3d0a4941727",
 "orderIdentifier": "",
 "groupTag": "",
 "serialNumber": "8645-5788-4128-6899-2408-1523-89",
 "productKey": "",
 "importId": "adaf2068-abf1-4309-906a-c9b4ef250670",
 "hardwareIdentifier": "",
 "assignedUserPrincipalName": ""
 "state": {
 "deviceImportStatus": "complete",
 "deviceRegistrationId": "f233b7c8-82bc-4839-8aed-6bbd98d7066f",
 "deviceErrorCode": 0,
 "deviceErrorName": "None"
 }}]} 

Autopilot Registration Completed?

This registration process can take any time up to 15 minutes. So what is happening here in the back end?

  • The POST call initiates the device registration to the Windows Autopilot service (DDS – Device Directory Service) using the hardware hash.
  • DDS generates a unique ID known as ZTDID (Zero Touch Device Id, ZTD is the codename for Windows Autopilot in MS) against the registration, which is returned to Intune (via the GET call to query the status, in the form of the “device registration id” GUID)

We see the device entry appearing under the Microsoft Intune > Device enrollment > Windows enrollment > Windows Autopilot devices.

Autopilot Device details -  Windows Autopilot Behind the Scenes
Autopilot Device details – Windows Autopilot Behind the Scenes 3

Completed? Not Completed?Azure AD Windows Autopilot Behind the Scenes

But the story doesn’t end here. Noticed the Associated Azure AD device object in the Autopilot device properties. The Device is yet to start the provisioning process, and there is an Azure AD object created already?

Azure AD device object -  Windows Autopilot Behind the Scenes
Azure AD device object – Windows Autopilot Behind the Scenes 4

Autopilot & Azure AD Device Registration Service (DRS)

With the Autopilot device object created, the Azure AD Device Registration Service (DRS) pre-creates an Azure AD device object stamping the ZTDID on it.

Audit log showing the Azure DRS action Add Device -  Windows Autopilot Behind the Scenes
Audit log showing the Azure DRS action Add Device – Windows Autopilot Behind the Scenes

This blank device object comes up under the Azure AD devices with the Serial Number of the Device as Name and, at the moment, is Disabled.

Pre-created Azure AD device object properties -  Windows Autopilot Behind the Scenes
Pre-created Azure AD device object properties – Windows Autopilot Behind the Scenes 5

What makes the Windows Autopilot device become a member of the dynamic device group created against the query of ZTDID?

If you have any prior knowledge of the Windows enrollment flows, in the case of Azure AD joining an Azure AD registration, you would know that Azure DRS creates the Azure AD device object after the user initiates the join/registration activity on the Device.

But, Windows Autopilot is an exception where the Azure AD device object is pre-created.

It is because of this Azure AD device object; that the Device becomes a member of the dynamic device group for Autopilot devices you create in Azure with the query (Device.devicePhysicalIDs -any _ -contains “[ZTDId]”).

Move Autopilot Device Registered to One Tenant to Another Tenant?

The DDS registration Id (ZTDID) is universally unique based on hardware hash. What will happen when you have a device registered to Autopilot from one tenant and plan to move it to another tenant?

You would need to delete the device entry from Autopilot devices of that first tenant which will trigger a deregistration call from Intune clearing the ZTDID from DDS.

This will help the same device registration with another tenant so that it can be added from another tenant. Failing to do so will always result in the below error.

Autopilot Error Code 808 – ZtdDeviceAssignedToOtherTenant. CSV line numbers affected

Error code 808 -  Windows Autopilot Behind the Scenes
Error code 808 – Windows Autopilot Behind the Scenes 6

With the Device becoming a member of the Autopilot device AAD group, we can continue to the next part.

Profile Configuration and Assignment

This is where you, as an IT Admin, will create the Autopilot deployment profile from under Microsoft Intune > Device enrollment > Windows enrollment > Windows Autopilot deployment profiles to configure the OOBE as per the deployment requirements.

Windows Autopilot Profile Configuration -  Windows Autopilot Behind the Scenes
Windows Autopilot Profile Configuration – Windows Autopilot Behind the Scenes 7

Graph Post Call with the Profile ID

The Graph call that works behind the scenes

RequestURL: https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles
 HTTP Version: HTTP/1.1
 Request method: POST
 Type: application/json
 Raw Content:
 {
 "@odata.type": "#microsoft.graph.azureADWindowsAutopilotDeploymentProfile",
 "displayName": "WhiteGlove",
 "description": "Test",
 "deviceNameTemplate": "JOY-%RAND:5%",
 "language": "",
 "enableWhiteGlove": true,
 "extractHardwareHash": false,
 "roleScopeTagIds": [],
 "outOfBoxExperienceSettings": {
 "deviceUsageType": "singleUser",
 "hideEscapeLink": true,
 "hidePrivacySettings": true,
 "hideEULA": true,
 "userType": "administrator",
 "skipKeyboardSelectionPage": false
 }
 } 

If everything is fine, the response to this POST call returns as 201 created. The content has the profile Id.

{
 "@odata.context": "https://graph.microsoft.com/beta/$metadata#deviceManagement/windowsAutopilotDeploymentProfiles/$entity",
 "@odata.type": "#microsoft.graph.azureADWindowsAutopilotDeploymentProfile",
 "id": "3e2b0a25-a332-4deb-83fb-60a0dc42e95e",
 "displayName": "WhiteGlove",
 "description": "Test",
 "language": "os-default",
 "createdDateTime": "2019-09-02T09:34:38.9238427Z",
 "lastModifiedDateTime": "2019-09-02T09:34:38.9238427Z",
 "enrollmentStatusScreenSettings": null,
 "extractHardwareHash": false,
 "deviceNameTemplate": "JOY-%RAND:5%",
 "deviceType": "windowsPc",
 "enableWhiteGlove": true,
 "roleScopeTagIds": [
 "0"
 ],
 "outOfBoxExperienceSettings": {
 "hidePrivacySettings": true,
 "hideEULA": true,
 "userType": "administrator",
 "deviceUsageType": "singleUser",
 "skipKeyboardSelectionPage": false,
 "hideEscapeLink": true
 }
 } 

Another Post Call – Assignment

The profile as created now waits for assignment to the group (the Autopilot dynamic device group). As the assignment is made, this is the Graph call that works behind the scenes.

 RequestURL: https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles/3e2b0a25-a332-4deb-83fb-60a0dc42e95e/assignments  
 HTTP Version: HTTP/1.1
 Request method: POST
 Post Data
 Type: application/json
 Raw Content:
 {
 "target": {
 "@odata.type": "#microsoft.graph.groupAssignmentTarget",
 "groupId": "6239fcb0-190e-431f-b618-b1510d71986a"
 }
 } 

I noticed the data type in the Graph call – the profile namespace is azureADWindowsAutopilotDeploymentProfile and is not under deviceManagement, which is Intune. Intune provides a UI to create the autopilot deployment profile but is not the endpoint from where the Device gets the profile.

Where does the Device gets the profile from? Check this terrific blog from Michael Niehaus.

Profile Assignment Status – Not Assigned – Assigning – Assigned?

The response 201 Created states if the assignment is successful. Once the profile is assigned to the group, the Profile status of the Autopilot device object changes from “Not Assigned” to “Assigning.”

Profile Status - Assigning -  Windows Autopilot Behind the Scenes
Profile Status – Assigning – Windows Autopilot Behind the Scenes 8

to finally “Assigned.”

Profile Status - Assigned -  Windows Autopilot Behind the Scenes
Profile Status – Assigned – Windows Autopilot Behind the Scenes 9

It is possible that you won’t see this status change as it requires a continuous refresh of the page.

Autopilot Profile Assignment from MSfB (Microsoft Store for Business)

For profiles created in MSfB or profiles assigned from MSfB, the Profile status of the Autopilot device object will show as “Externally Assigned.”

Why will the Device Skip Autopilot provisioning?

Deleting the pre-created Azure AD device object will result in the device skipping/failing Autopilot provisioning.

Only the Device object pre-created by Azure DRS during Autopilot (DDS) registration has the ZTDID stamped to it.

User-Driven

For user-driven mode, when the Device starts OOBE setup and gets connected to the network, it checks with Autopilot service for status and gets the profile downloaded accordingly.

Because of this, you will still get the custom Azure sign-in page as pre-negotiated.

But as you sign in to perform the auth and the Device reaches out to the Azure DRS endpoint to request the Join process, Azure DRS will fail to find a match for the corresponding Azure AD device object as provided in the request.

The provisioning, in this case, will not fail as Azure DRS will create a new device object. However, it will not have the ZTDID stamping. The provisioning will fall back to the consumer OOBE flow. The normal AAD join flow from OOBE.

More explanation is coming in the next post, Part 3 of this Autopilot series, so stay tuned!

Self-Deploy

For self-deploy mode, it will fail at the ESP stage Device preparation on the AADEnroll task since the service will fail to get the associated Azure device object.

This is a security mechanism to stop rogue devices from joining Azure AD with no credentials.

WhiteGlove?

The same is true for WhiteGlove enabled user-driven deployment because the WhiteGlove process is essentially carried out in the self-deploy mode process.

I will put more light on this in Part 4 of this series, specifically for WhiteGlove.

Enable Auto-Enrollment?

The profile is assigned, and the last thing that is needed to be done is to enable Auto-enrollment.

Enable MDM scope to allow Auto Enrollment -  Windows Autopilot Behind the Scenes
Enable MDM scope to allow Auto Enrollment – Windows Autopilot Behind the Scenes 10

Windows Autopilot Architecture Schema

This completes the Admin-End configuration phase of Windows Autopilot. If you have read this, you will be able to summarize the entire flow schematically, as shown below.

Windows Autopilot Behind the Scenes
Windows Autopilot – Admin End HLD – Windows Autopilot Behind the Scenes 11

To be Continued…

In my next post, I will continue discussing Windows Autopilot with respect to the device end implementation. Read something new every day, and learn something new every day!

  • DDS – Device Directory Service
  • ZTDID – Zero Touch Device Id
  • DRS – Device Registration Service

Resources

1 thought on “Windows Autopilot Behind The Scenes Secrets – Admin Side – Part 2”

  1. FYI –

    > The DDS registration Id (ZTDID) is universally unique based on hardware hash.

    ZTDID is just a randomly generated GUID that ties the AzureAD object with the Autopilot ID (ZTDID *is* the GUID of the Autopilot Object), there’s nothing special about it.

    If you look at the AzureAD devicePhysicalIDs array, the actual hardware hash is stored in [HWID].

    If you import a device into Autopilot using a CSV and then delete it, it will get a different ZTDID each time. However, the HWID will be constant.

    Reply

Leave a Comment

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