SyncML <Delete> Windows profiles (#42206)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33418 

Demo video: https://www.youtube.com/watch?v=gtsIYxmIOSo
Docs: https://github.com/fleetdm/fleet/pull/42269/changes

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Windows profiles now send SyncML <Delete> commands when profiles are
removed or hosts change teams, ensuring profile settings are removed
from devices like on macOS.
* Deletion is handled as a two-phase flow: pending removals are enqueued
and tracked instead of being immediately deleted.

* **Tests**
* Added/updated tests for delete-command generation, remove-status
mappings, and end-to-end removal reconciliation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Victor Lyuboslavsky
2026-03-26 18:25:54 -05:00
committed by GitHub
parent 534b1870c8
commit 4e7c6f33a7
12 changed files with 1387 additions and 177 deletions
@@ -161,9 +161,6 @@ func TestPreprocessWindowsProfileContentsForDeployment(t *testing.T) {
expectedContents: `<Replace><Item><Target><LocURI>./Device/Test</LocURI></Target><Data>Device Serial: $FLEET_VAR_HOST_HARDWARE_SERIAL</Data></Item></Replace>`,
expectError: true,
processingError: "Found 2 hosts with UUID test-uuid-789. Profile variable substitution for $FLEET_VAR_HOST_HARDWARE_SERIAL requires exactly one host",
expect: func(t *testing.T, managedCerts []*fleet.MDMManagedCertificate) {
require.True(t, ds.UpdateOrDeleteHostMDMWindowsProfileFuncInvoked)
},
setup: func() {
ds.ListHostsLiteByUUIDsFunc = func(ctx context.Context, filter fleet.TeamFilter, uuids []string) ([]*fleet.Host, error) {
require.Equal(t, []string{"test-uuid-789"}, uuids)
@@ -178,9 +175,6 @@ func TestPreprocessWindowsProfileContentsForDeployment(t *testing.T) {
},
}, nil
}
ds.UpdateOrDeleteHostMDMWindowsProfileFunc = func(ctx context.Context, profile *fleet.HostMDMWindowsProfile) error {
return nil
}
},
},
{