Skip to content

Add Windows managed local account server flow (#48721) - #49924

Merged
getvictor merged 35 commits into
mainfrom
victor-48721-windows-mla-server
Aug 3, 2026
Merged

Add Windows managed local account server flow (#48721)#49924
getvictor merged 35 commits into
mainfrom
victor-48721-windows-mla-server

Conversation

@getvictor

@getvictor getvictor commented Jul 24, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #48721

Part 2 of #43488

Checklist for submitter

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

Testing

  • Added/updated automated tests

  • QA'd all new/changed functionality manually

Database migrations

  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

Summary by CodeRabbit

  • New Features
    • Windows devices can now create and securely escrow managed local account passwords during enrollment.
    • Added Windows managed local account status and password availability to host details.
    • Device-reported setup errors are surfaced with helpful details.
    • Account creation is automatically requested when supported by the device, plan, and configuration.
  • Bug Fixes
    • Windows accounts are excluded from password rotation workflows.
    • Re-enrollment correctly triggers account creation when needed.
    • Passwords remain available when settings change after enrollment.

@getvictor

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@getvictor

Copy link
Copy Markdown
Member Author

/agentic_review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds the server-side support for Windows “managed local admin account” creation/escrow via Orbit, plus schema changes to reuse the existing managed-local-account password table for Windows rows.

Changes:

  • Advertise a new Orbit capability and send a new Orbit notification during Windows OOBE/ESP to trigger account creation + escrow.
  • Add a new Orbit escrow endpoint and service/datastore plumbing to persist a Windows device-generated password (command UUID nullable).
  • Extend host managed-account password retrieval to support Windows while keeping rotation macOS-only.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
server/service/orbit.go Adds Windows OOBE notification gating + new Orbit escrow service endpoint for Windows managed local account password.
server/service/hosts.go Populates managed local account status in Windows host details response.
server/service/handler.go Registers new Orbit route for managed local account escrow.
server/mock/service/service_mock.go Adds mock method for the new escrow service call.
server/mock/datastore_mock.go Adds mock method for the new datastore escrow save call.
server/fleet/service.go Extends service interface with EscrowWindowsManagedLocalAccountPassword.
server/fleet/orbit.go Adds OrbitConfigNotifications.CreateWindowsManagedLocalAccount flag.
server/fleet/datastore.go Extends datastore interface with SaveHostManagedLocalAccountFromEscrow.
server/fleet/capabilities.go Adds CapabilityWindowsManagedLocalAccount and advertises it for Windows Orbit clients.
server/fleet/api_orbit.go Adds request/response types for the new Orbit escrow endpoint.
server/datastore/mysql/schema.sql Updates schema dump: command_uuid becomes nullable in host_managed_local_account_passwords.
server/datastore/mysql/migrations/tables/20260724210609_RelaxManagedLocalAccountCommandUUID.go Migration to relax command_uuid to NULL.
server/datastore/mysql/migrations/tables/20260724210609_RelaxManagedLocalAccountCommandUUID_test.go Verifies migration allows NULL command_uuid inserts.
server/datastore/mysql/managed_local_account.go Adds SaveHostManagedLocalAccountFromEscrow upsert behavior for Windows escrow.
server/datastore/mysql/managed_local_account_test.go Adds tests for escrow-save semantics and exclusion from auto-rotation selection.
ee/server/service/hosts.go Allows Windows in GetHostManagedAccountPassword while keeping rotation macOS-only.
Comments suppressed due to low confidence (1)

server/service/orbit.go:1537

  • This new escrow service method introduces multiple validation/gating branches (missing host context, not an enrolled Windows MDM host, clientError short-circuit, empty/too-long password, setting checks, datastore save + activity). There are tests for EscrowLUKSData in orbit_test.go, but none for EscrowWindowsManagedLocalAccountPassword; adding unit tests would help prevent regressions.
func (svc *Service) EscrowWindowsManagedLocalAccountPassword(ctx context.Context, password string, clientError string) error {
	// this is not a user-authenticated endpoint
	svc.authz.SkipAuthorization(ctx)

	host, ok := hostctx.FromContext(ctx)
	if !ok {
		return newOsqueryError("internal error: missing host from request context")
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/service/handler.go Outdated
Comment thread server/service/orbit.go Outdated
Comment thread server/service/orbit.go Outdated
Comment thread server/service/orbit.go Outdated
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 24, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Failed escrow blocks retries ✓ Resolved 🐞 Bug ≡ Correctness
Description
GetOrbitConfig stops requesting Windows managed local account creation whenever the stored
windows_mdm_device_id matches the current enrollment, even if the managed local account row is
later marked status='failed'. This prevents automatic retry/self-heal for the current enrollment
and can leave the host stuck without an available password until some external state change (e.g.,
re-enrollment) occurs.
Code

server/fleet/microsoft_mdm.go[R918-922]

+// HasEscrowedManagedLocalAccountForCurrentEnrollment reports whether the host has already escrowed a managed local account password for the
+// enrollment it is currently on. A host that escrowed under a previous enrollment (it was wiped and re-enrolled) counts as not having one:
+// the account no longer exists on the machine, so the stored password is stale and must be replaced.
+func (s MDMWindowsHostConfigState) HasEscrowedManagedLocalAccountForCurrentEnrollment() bool {
+	return s.ManagedLocalAccountDeviceID != nil && *s.ManagedLocalAccountDeviceID == s.MDMDeviceID
Evidence
The retry gate uses only windows_mdm_device_id equality and ignores MLA status; because failures
do not clear windows_mdm_device_id, a failed row after a prior success still looks escrowed and
suppresses further create requests even though the code comments state failures should keep being
asked.

server/fleet/microsoft_mdm.go[900-923]
server/datastore/mysql/microsoft_mdm.go[264-298]
server/datastore/mysql/managed_local_account.go[70-89]
server/service/orbit.go[613-631]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Windows MLA provisioning retries are gated by `HasEscrowedManagedLocalAccountForCurrentEnrollment`, which currently only compares enrollment device IDs. If a host first escrows successfully (so `windows_mdm_device_id` is set) and later reports an error (row becomes `status='failed'`), the device-id check still returns true and the server stops sending `CreateWindowsManagedLocalAccount`, contradicting the intended “failed keeps being asked” behavior.
### Issue Context
- `ReportManagedLocalAccountEscrowError` updates `status` and `client_error` but does not clear `windows_mdm_device_id`.
- `GetMDMWindowsHostConfigState` only selects `mla.windows_mdm_device_id` and does not include MLA status.
- `HasEscrowedManagedLocalAccountForCurrentEnrollment` only checks device-id equality.
### Fix Focus Areas
- server/fleet/microsoft_mdm.go[900-923]
- server/datastore/mysql/microsoft_mdm.go[264-298]
- server/datastore/mysql/managed_local_account.go[70-89]
- server/service/orbit.go[613-631]
### Suggested fix
1. Extend `GetMDMWindowsHostConfigState` to also return the managed local account status (e.g., `mla.status`), or compute an “escrow valid” boolean in SQL (e.g., only return `windows_mdm_device_id` when `mla.status <> 'failed'` and/or `encrypted_password IS NOT NULL`).
2. Update `MDMWindowsHostConfigState` and `HasEscrowedManagedLocalAccountForCurrentEnrollment()` to require both:
- the device-id matches current enrollment, and
- the MLA record is not failed (and ideally has a password).
3. Add/extend a unit test: simulate successful escrow (device-id matches), then report failure, then confirm `CreateWindowsManagedLocalAccount` is set again for the same enrollment.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Escrow blocked by config read ✓ Resolved 🐞 Bug ☼ Reliability
Description
EscrowWindowsManagedLocalAccountPassword aborts before saving the password if reading
AppConfig/TeamMDMConfig fails, even though the code comment says escrow must proceed to avoid
orphaning the on-device account. This can drop the only recoverable password on transient
config-read failures (setting-disabled is handled correctly; only read failures block escrow).
Code

server/service/orbit.go[R1576-1585]

+	// The setting or license may have changed between the notification and this escrow. The account
+	// already exists on the device, so rejecting would orphan it with an unrecoverable password. Store
+	// the password regardless and only log a warning when the setting is no longer enabled.
+	appConfig, err := svc.ds.AppConfig(ctx)
+	if err != nil {
+		return ctxerr.Wrap(ctx, err, "load app config for managed local account escrow")
+	}
+	if enabled, err := svc.windowsManagedLocalAccountEnabled(ctx, host, appConfig); err != nil {
+		return ctxerr.Wrap(ctx, err, "check windows managed local account setting for escrow")
+	} else if !enabled {
Evidence
The function states it must store the password regardless to avoid orphaning, but it calls
svc.ds.AppConfig and svc.windowsManagedLocalAccountEnabled first and returns their errors
immediately, which prevents reaching the SaveHostManagedLocalAccountFromEscrow call.

server/service/orbit.go[1576-1592]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`EscrowWindowsManagedLocalAccountPassword` is intended to **always persist** the escrowed password to avoid orphaning the on-device managed local admin account, even if settings/license changed. However, it performs `AppConfig` + team setting lookup **before** saving and returns errors from those reads, which prevents password persistence.
### Issue Context
- The comment explicitly states escrow must proceed to avoid orphaning.
- The setting check is only used to decide whether to log a warning; it should not be able to block persistence.
### Fix approach
- Persist the password first (`SaveHostManagedLocalAccountFromEscrow`).
- Make the config/team-setting check best-effort *after* saving:
- If config/team lookup succeeds and is disabled: warn (as today).
- If config/team lookup fails: log a warning/error but **do not fail** the escrow.
### Fix Focus Areas
- server/service/orbit.go[1576-1592]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Platform gate blocks Windows 🐞 Bug ≡ Correctness
Description
GetHostManagedAccountPassword returns 400 unless host.Platform is macOS or exactly "windows",
so a Windows host with an empty/unknown platform value is rejected even if it already escrowed a
managed local account password. This can block password retrieval during early Windows OOBE until
osquery updates the platform field.
Code

ee/server/service/hosts.go[R760-765]

+	isWindows := host.Platform == "windows"
+	if !fleet.IsMacOSPlatform(host.Platform) && !isWindows {
return nil, &fleet.BadRequestError{
-			Message: "Host is not a macOS device.",
+			Message: "Host is not a macOS or Windows device.",
}
}
Evidence
The retrieval endpoint hard-rejects non-macOS/non-windows platforms, while the escrow flow
explicitly documents that host.Platform can be empty during early Windows OOBE and therefore
avoids using it; HostLite reads the platform column directly, so an empty DB value propagates
into the failing guard.

ee/server/service/hosts.go[747-765]
server/service/orbit.go[1539-1546]
server/datastore/mysql/hosts.go[5769-5809]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ee/server/service/hosts.go:GetHostManagedAccountPassword` uses `host.Platform == "windows"` to allow the Windows path. The Windows escrow flow explicitly supports early OOBE where `host.Platform` may be empty, so this gate can incorrectly reject valid Windows MDM hosts.
### Issue Context
The escrow endpoint verifies Windows eligibility via Windows MDM enrollment (not `host.Platform`) because platform can be empty early in OOBE. The retrieval endpoint should use the same eligibility signal (e.g., Windows MDM enrollment / presence of a Windows managed-local-account record) rather than requiring `hosts.platform` to already be populated.
### Fix Focus Areas
- ee/server/service/hosts.go[747-826]
- server/service/orbit.go[1539-1546]
- server/datastore/mysql/hosts.go[5769-5809]
### Suggested fix
- Replace/augment `isWindows := host.Platform == "windows"` with a fallback check based on Windows MDM enrollment (e.g., call `svc.ds.MDMWindowsGetEnrolledDeviceWithHostUUID(ctx, host.UUID)` and treat success as Windows eligibility).
- Only return the “not a macOS or Windows device” error after both (a) macOS platform check and (b) Windows MDM enrollment check fail.
- Add a regression test covering a host with empty `hosts.platform` but Windows MDM-enrolled + escrowed MLA password, verifying the endpoint returns the password.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Duplicate TeamMDMConfig query 🐞 Bug ➹ Performance
Description
For team-scoped Windows hosts, GetOrbitConfig can load TeamMDMConfig once via
windowsManagedLocalAccountEnabled and then load it again later in the same request path. This adds
avoidable DB work on a hot endpoint.
Code

server/service/orbit.go[R623-630]

+			if mlaCapable && !state.HasEscrowedManagedLocalAccountForCurrentEnrollment() {
+				if lic, _ := license.FromContext(ctx); lic != nil && lic.IsPremium() {
+					enabled, err := svc.windowsManagedLocalAccountEnabled(ctx, host, appConfig)
+					if err != nil {
+						return fleet.OrbitConfig{}, ctxerr.Wrap(ctx, err, "checking windows managed local account setting")
+					}
+					notifs.CreateWindowsManagedLocalAccount = enabled
+				}
Evidence
The managed local account enablement helper performs a TeamMDMConfig read, and GetOrbitConfig later
performs another TeamMDMConfig read for team hosts in the same function, creating a duplicate query
in the same request path.

server/service/orbit.go[613-631]
server/service/orbit.go[861-875]
server/service/orbit.go[705-708]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GetOrbitConfig` may call `TeamMDMConfig` twice for the same request when evaluating Windows managed local account enablement for team hosts.
### Issue Context
- `GetOrbitConfig` calls `windowsManagedLocalAccountEnabled`, which calls `ds.TeamMDMConfig` for team hosts.
- Later, the team-specific `GetOrbitConfig` branch calls `ds.TeamMDMConfig` again.
### Fix Focus Areas
- server/service/orbit.go[613-631]
- server/service/orbit.go[861-875]
- server/service/orbit.go[705-708]
### Suggested fix
- Fetch `TeamMDMConfig` once for team hosts (or thread the already-fetched value through), and use that single value both for the MLA enablement check and for later team MDM logic.
- Alternatively, move MLA enablement evaluation into the team branch where `TeamMDMConfig` is already fetched, and handle no-team hosts separately via AppConfig.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Unbounded client_error logging ✓ Resolved 🐞 Bug ☼ Reliability
Description
EscrowWindowsManagedLocalAccountPassword logs the device-provided client_error string without an
application-level length limit or truncation. A malformed/malicious Orbit client can send an
oversized client_error, producing excessively large log entries and unnecessary logging/ingestion
overhead.
Code

server/service/orbit.go[R1548-1552]

+	// A device-side failure is logged and nothing is recorded.
+	if clientError != "" {
+		svc.logger.WarnContext(ctx, "fleetd reported an error creating the windows managed local account",
+			"host_id", host.ID, "host_uuid", host.UUID, "client_error", clientError)
+		return nil
Evidence
The request type exposes client_error as a plain string from the device, and the service logs it
directly when present; the only explicit input bound in this handler is for password, not for
client_error.

server/fleet/api_orbit.go[272-279]
server/service/orbit.go[1526-1560]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Orbit managed-local-account escrow endpoint accepts `client_error` from the device and logs it directly. Unlike `password`, it has no max-length validation, so very large values can reach structured logging.
### Issue Context
- Request schema allows arbitrary `client_error`.
- Server caps password length (`managedLocalAccountMaxPasswordLength`) but not `client_error`.
### Fix Focus Areas
- server/service/orbit.go[1526-1560]
- server/fleet/api_orbit.go[272-279]
### Suggested fix
- Introduce a `managedLocalAccountMaxClientErrorLength` constant (e.g., 1024 or similar).
- Before logging, either:
- truncate `clientError` to that limit and log an additional field like `client_error_truncated=true`, or
- return a `BadRequestError` when it exceeds the limit (truncation is usually preferable for error-reporting paths).
- Add a unit test to ensure oversized `client_error` values are truncated (or rejected) deterministically.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread ee/server/service/hosts.go Outdated
Comment thread server/service/orbit.go Outdated
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

Walkthrough

Adds Windows managed local account capability advertisement, Orbit configuration notification, password escrow and error reporting, enrollment-aware persistence, and host status exposure. Windows passwords can be retrieved without rotation metadata, while rotation remains rejected. Datastore schema and queries support nullable passwords, escrow errors, and enrollment identifiers. Tests cover notification gating, escrow validation, error handling, re-enrollment, host details, and auto-rotation exclusion.

Possibly related issues

Possibly related PRs

  • fleetdm/fleet#49863 — Adds the managed-local-account setting plumbing used by this PR’s notification and escrow gating.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Clear, specific title matching the Windows managed local account server-flow changes.
Description check ✅ Passed It includes the related issue, testing, and database migration sections, and mostly follows the template.
Linked Issues check ✅ Passed The changes implement the Windows Orbit notification, escrow endpoint, host retrieval/status, and Windows rotation guard required by #48721.
Out of Scope Changes check ✅ Passed No obvious unrelated code changes stand out; the diff stays focused on the Windows managed local account flow.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch victor-48721-windows-mla-server

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.48352% with 61 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.14%. Comparing base (1dc1a51) to head (2150de1).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
server/service/orbit.go 59.42% 20 Missing and 8 partials ⚠️
ee/server/service/hosts.go 33.33% 3 Missing and 3 partials ⚠️
server/datastore/mysql/managed_local_account.go 84.61% 3 Missing and 3 partials ⚠️
...1213352_ManagedLocalAccountWindowsEscrowColumns.go 66.66% 4 Missing and 2 partials ⚠️
server/fleet/api_orbit.go 0.00% 6 Missing ⚠️
server/service/hosts.go 45.45% 3 Missing and 3 partials ⚠️
server/datastore/mysql/microsoft_mdm.go 90.00% 1 Missing and 1 partial ⚠️
server/fleet/capabilities.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49924      +/-   ##
==========================================
- Coverage   68.20%   68.14%   -0.07%     
==========================================
  Files        3943     3936       -7     
  Lines      251273   251426     +153     
  Branches    13406    13391      -15     
==========================================
- Hits       171390   171341      -49     
- Misses      64532    64720     +188     
- Partials    15351    15365      +14     
Flag Coverage Δ
backend 69.50% <66.48%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@getvictor

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@getvictor

Copy link
Copy Markdown
Member Author

/agentic_review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Comment thread server/service/orbit.go Outdated
Copilot AI review requested due to automatic review settings July 29, 2026 16:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

server/service/orbit.go:1580

  • The escrow endpoint doesn’t enforce Fleet Premium, so a Windows MDM host could escrow a managed local account password even when the server is running with a Free license. The linked requirements for this feature specify premium gating, and it’s also important to avoid storing this sensitive material when the feature isn’t licensed.
	// Eligibility is the host's Windows MDM enrollment
	if _, err := svc.ds.MDMWindowsGetEnrolledDeviceWithHostUUID(ctx, host.UUID); err != nil {
		if fleet.IsNotFound(err) {
			return &fleet.BadRequestError{Message: "managed local account escrow is only supported for Windows MDM hosts"}
		}
		return ctxerr.Wrap(ctx, err, "verify windows mdm enrollment for managed local account escrow")
	}

server/service/orbit.go:619

  • This sets CreateWindowsManagedLocalAccount even when the host is not in OOBE (AwaitingConfiguration=None). The linked issue’s acceptance criteria specify this notification should only be set during the OOBE/setup phase (AwaitingConfiguration Pending/Active) to avoid asking already-provisioned devices to recreate/reset the account outside enrollment.
			// Ask a capable premium fleetd to create and escrow the Windows managed local admin account when the host's fleet has the
			// setting enabled. The request stops once the host escrows a password for this enrollment. Re-enrolling deletes the enrollment
			// row and with it the flag, so a re-imaged device is asked again.
			if mlaCapable && !state.ManagedLocalAccountEscrowed {
				if lic, _ := license.FromContext(ctx); lic != nil && lic.IsPremium() {

server/fleet/orbit.go:61

  • The comment for CreateWindowsManagedLocalAccount says it’s set for any Windows MDM host “not only during OOBE”, but the linked issue/task description specifies the notification should only be set during OOBE (AwaitingConfiguration Pending/Active). The comment should match the intended gating to avoid misleading fleetd/server implementers.
	// CreateWindowsManagedLocalAccount tells fleetd on Windows to create the hidden managed local admin account and escrow its password.
	// Set for any Windows MDM host whose fleet has the setting enabled, not only during OOBE, for hosts whose fleetd advertises
	// CapabilityWindowsManagedLocalAccount, and until the host has escrowed a password for its current enrollment.

pkg/str/str.go:39

  • TruncateRunes will panic if maxRunes is negative (slice bounds). Since this is a general-purpose helper that may get reused, it should defensively handle maxRunes <= 0 to avoid surprising panics from bad inputs.
func TruncateRunes(s string, maxRunes int) string {
	if len(s) <= maxRunes {
		// Fast path: a string of at most maxRunes bytes cannot exceed maxRunes characters.
		return s
	}
	if utf8.RuneCountInString(s) <= maxRunes {
		return s
	}
	return string([]rune(s)[:maxRunes])

@getvictor getvictor assigned sharon-fdm and unassigned lucasmrod Jul 30, 2026
sharon-fdm
sharon-fdm previously approved these changes Jul 31, 2026

@sharon-fdm sharon-fdm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks solid. Nice work on the idempotent design.

Copilot AI review requested due to automatic review settings July 31, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.

Suppressed comments (3)

server/service/orbit.go:619

  • This sets CreateWindowsManagedLocalAccount even when awaiting_configuration is None. The linked issue (#48721) specifies only sending the notification during OOBE (awaiting_configuration Pending/Active) to avoid asking already-provisioned/fully enrolled devices to create the account outside setup. Consider scoping the notification to Pending/Active (or update the story/docs if the broader behavior is intended).
			// Ask a capable premium fleetd to create and escrow the Windows managed local admin account when the host's fleet has the
			// setting enabled. The request stops once the host escrows a password for this enrollment. Re-enrolling deletes the enrollment
			// row and with it the flag, so a re-imaged device is asked again.
			if mlaCapable && !state.ManagedLocalAccountEscrowed {
				if lic, _ := license.FromContext(ctx); lic != nil && lic.IsPremium() {

server/service/orbit.go:1623

  • EscrowWindowsManagedLocalAccountPassword currently persists the password even if the premium license or managed-local-account setting has been turned off after the device was notified (it only logs a warning). The linked issue (#48721) calls for rejecting escrow when the setting is off and/or the license is not premium, so this behavior needs an explicit decision: either enforce rejection here (and update tests), or update the story/docs to reflect the “always store to avoid orphaning the on-device account” approach.
	// The setting or license may have changed between the notification and this escrow. That does not change what we
	// store, only whether it is worth flagging, so this check is best-effort.
	if appConfig, err := svc.ds.AppConfig(ctx); err != nil {
		svc.logger.ErrorContext(ctx, "load app config to check managed local account setting after escrow", "err", err)
	} else if enabled, err := svc.windowsManagedLocalAccountEnabled(ctx, host, appConfig); err != nil {

pkg/str/str.go:34

  • TruncateRunes can panic when maxRunes <= 0 (negative slice bound in []rune(s)[:maxRunes]). Since this is a general utility, it should defensively handle non-positive limits.
func TruncateRunes(s string, maxRunes int) string {
	if len(s) <= maxRunes {
		// Fast path: a string of at most maxRunes bytes cannot exceed maxRunes characters.
		return s
	}

sharon-fdm
sharon-fdm previously approved these changes Aug 3, 2026
…mla-server

# Conflicts:
#	server/service/orbit_test.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.

Suppressed comments (4)

server/service/orbit.go:1597

  • EscrowWindowsManagedLocalAccountPassword currently only checks for a Windows MDM enrollment row, then proceeds to store passwords (and log activity) without enforcing premium license and/or the managed-local-account setting. The linked issue text calls out rejecting escrow when the team setting is off and when the license is not premium. Please confirm the desired server-side enforcement; if enforcement is required, add explicit checks here (and adjust the tests that expect escrow to succeed when the setting is toggled off).
	// Eligibility is the host's Windows MDM enrollment
	if _, err := svc.ds.MDMWindowsGetEnrolledDeviceWithHostUUID(ctx, host.UUID); err != nil {
		if fleet.IsNotFound(err) {
			return &fleet.BadRequestError{Message: "managed local account escrow is only supported for Windows MDM hosts"}
		}

pkg/str/str.go:39

  • TruncateRunes can panic when maxRunes <= 0 (negative slice bound in []rune(s)[:maxRunes]). Since this is an exported helper, it should defensively handle non-positive limits.
func TruncateRunes(s string, maxRunes int) string {
	if len(s) <= maxRunes {
		// Fast path: a string of at most maxRunes bytes cannot exceed maxRunes characters.
		return s
	}
	if utf8.RuneCountInString(s) <= maxRunes {
		return s
	}
	return string([]rune(s)[:maxRunes])

server/datastore/mysql/managed_local_account.go:121

  • GetHostManagedLocalAccountPassword treats a NULL/empty encrypted_password as not-found, but GetHostManagedLocalAccountStatus considers any non-NULL blob as "has_password". Using LENGTH(...) keeps status/password_available consistent even if an empty blob ever gets persisted.
	const stmt = `
		SELECT
			status,
			client_error,
			encrypted_password IS NOT NULL AS has_password,
			pending_encrypted_password IS NOT NULL AS pending_rotation,
			auto_rotate_at

server/service/orbit.go:638

  • This sets CreateWindowsManagedLocalAccount for any connected Windows MDM host whenever the capability is present and escrow hasn’t happened yet. The linked issue’s acceptance criteria specify sending this only during OOBE (awaiting_configuration Pending/Active). Please confirm the intended scope and either (a) move this under the Pending/Active branch, or (b) update the story/requirements to match the broader behavior (and ensure fleet/orbit.go’s field comment stays accurate).
			// Ask a capable premium fleetd to create and escrow the Windows managed local admin account when the host's fleet has the
			// setting enabled. The request stops once the host escrows a password for this enrollment. Re-enrolling deletes the enrollment
			// row and with it the flag, so a re-imaged device is asked again.
			if mlaCapable && !state.ManagedLocalAccountEscrowed {
				if lic, _ := license.FromContext(ctx); lic != nil && lic.IsPremium() {

@getvictor
getvictor merged commit 98060b0 into main Aug 3, 2026
36 checks passed
@getvictor
getvictor deleted the victor-48721-windows-mla-server branch August 3, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows local admin account: orbit notification, password escrow, and host endpoints

4 participants