Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion server/datastore/mysql/microsoft_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ func (ds *Datastore) MDMWindowsGetEnrolledDeviceWithDeviceID(ctx context.Context
enroll_proto_version,
enroll_client_version,
not_in_oobe,
awaiting_configuration,
awaiting_configuration_at,
credentials_hash,
credentials_acknowledged,
created_at,
Expand Down Expand Up @@ -99,6 +101,8 @@ func (ds *Datastore) MDMWindowsGetEnrolledDeviceWithHostUUID(ctx context.Context
enroll_proto_version,
enroll_client_version,
not_in_oobe,
awaiting_configuration,
awaiting_configuration_at,
credentials_hash,
credentials_acknowledged,
created_at,
Expand Down Expand Up @@ -132,11 +136,13 @@ func (ds *Datastore) MDMWindowsInsertEnrolledDevice(ctx context.Context, device
enroll_proto_version,
enroll_client_version,
not_in_oobe,
awaiting_configuration,
awaiting_configuration_at,
host_uuid,
credentials_hash,
credentials_acknowledged)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
mdm_device_id = VALUES(mdm_device_id),
device_state = VALUES(device_state),
Expand All @@ -147,6 +153,8 @@ func (ds *Datastore) MDMWindowsInsertEnrolledDevice(ctx context.Context, device
enroll_proto_version = VALUES(enroll_proto_version),
enroll_client_version = VALUES(enroll_client_version),
not_in_oobe = VALUES(not_in_oobe),
awaiting_configuration = VALUES(awaiting_configuration),
awaiting_configuration_at = VALUES(awaiting_configuration_at),
host_uuid = VALUES(host_uuid),
credentials_hash = VALUES(credentials_hash),
credentials_acknowledged = VALUES(credentials_acknowledged)
Expand All @@ -164,6 +172,8 @@ func (ds *Datastore) MDMWindowsInsertEnrolledDevice(ctx context.Context, device
device.MDMEnrollProtoVersion,
device.MDMEnrollClientVersion,
device.MDMNotInOOBE,
device.AwaitingConfiguration,
device.AwaitingConfigurationAt,
device.HostUUID,
device.CredentialsHash,
device.CredentialsAcknowledged)
Expand Down
25 changes: 25 additions & 0 deletions server/datastore/mysql/microsoft_mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ func testMDMWindowsEnrolledDevice(t *testing.T, ds *Datastore) {
require.NotZero(t, gotEnrolledDevice.CreatedAt)
require.Equal(t, enrolledDevice.MDMDeviceID, gotEnrolledDevice.MDMDeviceID)
require.Equal(t, enrolledDevice.MDMHardwareID, gotEnrolledDevice.MDMHardwareID)
require.Equal(t, fleet.WindowsMDMAwaitingConfigurationNone, gotEnrolledDevice.AwaitingConfiguration)
require.Nil(t, gotEnrolledDevice.AwaitingConfigurationAt)

err = ds.MDMWindowsDeleteEnrolledDeviceOnReenrollment(ctx, enrolledDevice.MDMHardwareID)
require.NoError(t, err)
Expand Down Expand Up @@ -127,6 +129,29 @@ func testMDMWindowsEnrolledDevice(t *testing.T, ds *Datastore) {

err = ds.MDMWindowsDeleteEnrolledDeviceOnReenrollment(ctx, enrolledDevice.MDMHardwareID)
require.ErrorAs(t, err, &nfe)

// Test that awaiting configuration is persisted and updated on upsert.
now := time.Now().UTC()
enrolledDevice.AwaitingConfiguration = fleet.WindowsMDMAwaitingConfigurationPending
enrolledDevice.AwaitingConfigurationAt = &now
err = ds.MDMWindowsInsertEnrolledDevice(ctx, enrolledDevice)
require.NoError(t, err)

gotEnrolledDevice, err = ds.MDMWindowsGetEnrolledDeviceWithDeviceID(ctx, enrolledDevice.MDMDeviceID)
require.NoError(t, err)
require.Equal(t, fleet.WindowsMDMAwaitingConfigurationPending, gotEnrolledDevice.AwaitingConfiguration)
require.NotNil(t, gotEnrolledDevice.AwaitingConfigurationAt)

// Re-enroll clears awaiting configuration via upsert.
enrolledDevice.AwaitingConfiguration = fleet.WindowsMDMAwaitingConfigurationNone
enrolledDevice.AwaitingConfigurationAt = nil
err = ds.MDMWindowsInsertEnrolledDevice(ctx, enrolledDevice)
require.NoError(t, err)

gotEnrolledDevice, err = ds.MDMWindowsGetEnrolledDeviceWithDeviceID(ctx, enrolledDevice.MDMDeviceID)
require.NoError(t, err)
require.Equal(t, fleet.WindowsMDMAwaitingConfigurationNone, gotEnrolledDevice.AwaitingConfiguration)
require.Nil(t, gotEnrolledDevice.AwaitingConfigurationAt)
}

func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tables

import (
"database/sql"
"fmt"
)

func init() {
MigrationClient.AddMigration(Up_20260406120000, Down_20260406120000)
}

func Up_20260406120000(tx *sql.Tx) error {
if columnExists(tx, "mdm_windows_enrollments", "awaiting_configuration") {
return nil
}
_, err := tx.Exec(`
ALTER TABLE mdm_windows_enrollments
ADD COLUMN awaiting_configuration TINYINT(1) NOT NULL DEFAULT 0,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For whatever it's worth my initial thinking for this was this was almost a state based thing. I made it a tinyint to not make the windows enrollments rows too big but in Magnus's POC he did the same where essentially it was 0=not waiting, 1=initial waiting state, 2=later state which is basically once the full initialization/enrollment has completed and things have actually started

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Updated to 3 states

ADD COLUMN awaiting_configuration_at DATETIME(6) DEFAULT NULL
`)
if err != nil {
return fmt.Errorf("failed to add awaiting_configuration columns to mdm_windows_enrollments: %w", err)
}
return nil
}

func Down_20260406120000(tx *sql.Tx) error {
return nil
}
6 changes: 4 additions & 2 deletions server/datastore/mysql/schema.sql

Large diffs are not rendered by default.

47 changes: 31 additions & 16 deletions server/fleet/microsoft_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,23 +820,38 @@ func (msg WapProvisioningDoc) GetEncodedB64Representation() (string, error) {
/// MDMWindowsEnrolledDevice type
/// Contains the information of the enrolled Windows host

// WindowsMDMAwaitingConfiguration represents the state of a Windows device's setup experience.
type WindowsMDMAwaitingConfiguration uint

const (
// WindowsMDMAwaitingConfigurationNone means the device is not awaiting configuration (default, or setup complete/failed).
WindowsMDMAwaitingConfigurationNone WindowsMDMAwaitingConfiguration = 0
// WindowsMDMAwaitingConfigurationPending means the device enrolled via autopilot in OOBE and is waiting for orbit
// to register and setup experience items to be enqueued.
WindowsMDMAwaitingConfigurationPending WindowsMDMAwaitingConfiguration = 1
// WindowsMDMAwaitingConfigurationActive means ESP commands have been enqueued and setup progress is being tracked.
WindowsMDMAwaitingConfigurationActive WindowsMDMAwaitingConfiguration = 2
)

type MDMWindowsEnrolledDevice struct {
ID uint `db:"id"`
HostUUID string `db:"host_uuid"`
MDMDeviceID string `db:"mdm_device_id"`
MDMHardwareID string `db:"mdm_hardware_id"`
MDMDeviceState string `db:"device_state"`
MDMDeviceType string `db:"device_type"`
MDMDeviceName string `db:"device_name"`
MDMEnrollType string `db:"enroll_type"`
MDMEnrollUserID string `db:"enroll_user_id"`
MDMEnrollProtoVersion string `db:"enroll_proto_version"`
MDMEnrollClientVersion string `db:"enroll_client_version"`
MDMNotInOOBE bool `db:"not_in_oobe"`
CredentialsHash *[]byte `db:"credentials_hash"`
CredentialsAcknowledged bool `db:"credentials_acknowledged"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID uint `db:"id"`
HostUUID string `db:"host_uuid"`
MDMDeviceID string `db:"mdm_device_id"`
MDMHardwareID string `db:"mdm_hardware_id"`
MDMDeviceState string `db:"device_state"`
MDMDeviceType string `db:"device_type"`
MDMDeviceName string `db:"device_name"`
MDMEnrollType string `db:"enroll_type"`
MDMEnrollUserID string `db:"enroll_user_id"`
MDMEnrollProtoVersion string `db:"enroll_proto_version"`
MDMEnrollClientVersion string `db:"enroll_client_version"`
MDMNotInOOBE bool `db:"not_in_oobe"`
AwaitingConfiguration WindowsMDMAwaitingConfiguration `db:"awaiting_configuration"`
AwaitingConfigurationAt *time.Time `db:"awaiting_configuration_at"`
CredentialsHash *[]byte `db:"credentials_hash"`
CredentialsAcknowledged bool `db:"credentials_acknowledged"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}

func (e MDMWindowsEnrolledDevice) AuthzType() string {
Expand Down
16 changes: 16 additions & 0 deletions server/service/microsoft_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,20 @@ func (svc *Service) storeWindowsMDMEnrolledDevice(ctx context.Context, userID st
reqNotInOOBE = true
}

// Determine if the device is awaiting configuration. Set to Pending when the enrollment is
// non-programmatic (Autopilot via JWT/WSTEP, not orbit node key) AND the device is in OOBE
// (NotInOobe is false, since the field name is inverted). Later phases transition to Active
// (ESP commands enqueued) and back to None (setup complete/failed).
awaitingConfiguration := fleet.WindowsMDMAwaitingConfigurationNone
var awaitingConfigurationAt *time.Time
isProgrammatic := hostUUID != ""
isInOOBE := !reqNotInOOBE
if !isProgrammatic && isInOOBE {
awaitingConfiguration = fleet.WindowsMDMAwaitingConfigurationPending
now := time.Now().UTC()
awaitingConfigurationAt = &now
}

// Getting the Windows Enrolled Device Information
enrolledDevice := &fleet.MDMWindowsEnrolledDevice{
MDMDeviceID: reqDeviceID,
Expand All @@ -2108,6 +2122,8 @@ func (svc *Service) storeWindowsMDMEnrolledDevice(ctx context.Context, userID st
MDMEnrollProtoVersion: reqEnrollVersion,
MDMEnrollClientVersion: reqAppVersion,
MDMNotInOOBE: reqNotInOOBE,
AwaitingConfiguration: awaitingConfiguration,
AwaitingConfigurationAt: awaitingConfigurationAt,
HostUUID: hostUUID,
CredentialsHash: &credentialsHash,
CredentialsAcknowledged: true,
Expand Down
Loading