mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
OpenSpec example
This commit is contained in:
parent
378fc322cb
commit
62ce889e83
6 changed files with 619 additions and 0 deletions
33
openspec/README.md
Normal file
33
openspec/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# OpenSpec example for Fleet story #38785
|
||||
|
||||
Strict OpenSpec format. Illustrative example of what a full spec would look like if written before
|
||||
implementation of story fleetdm/fleet#38785.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
openspec/
|
||||
└── changes/
|
||||
└── windows-setup-cancel-if-software-fails/
|
||||
├── proposal.md # Intent, Scope, Approach
|
||||
├── design.md # Technical approach bullets
|
||||
├── tasks.md # Numbered categories with N.M task items
|
||||
└── specs/
|
||||
└── mdm-windows-setup-experience/
|
||||
└── spec.md # ADDED / MODIFIED / REMOVED requirements with GIVEN/WHEN/THEN scenarios
|
||||
```
|
||||
|
||||
## How strict OpenSpec answers the six behavioral contract questions
|
||||
|
||||
| Question | Answered in |
|
||||
|-----------------------------------|----------------------------------------------------------------------|
|
||||
| What goes in? | spec.md, GIVEN clauses |
|
||||
| What comes out? | spec.md, THEN clauses |
|
||||
| What must stay true? | spec.md, SHALL statements plus MODIFIED Requirements |
|
||||
| What side effects? | spec.md, scenarios for activity emission and DB state changes |
|
||||
| How does it fail? | spec.md, timeout and failed install scenarios |
|
||||
| What is explicitly out of bounds? | Not expressible. Strict OpenSpec has no non-goals section. |
|
||||
| Which packages / files? | Not expressible. Strict design.md is decisions only, not file lists. |
|
||||
|
||||
The last two gaps are the honest cost of staying strict. Teams that want non-goals and package lists have to
|
||||
extend the format (or pair it with an ADR).
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Design: Windows setup experience, cancel if software fails
|
||||
|
||||
## Technical approach
|
||||
- Reuse setup_experience_status_results and the canceled_setup_experience activity; no parallel Windows tables
|
||||
- Store awaiting_configuration on mdm_windows_enrollments directly, not a sibling table, since the SyncML
|
||||
checkin already joins that row
|
||||
- Tri-state awaiting_configuration: 0 not awaiting, 1 ESP not yet issued, 2 ESP issued and in progress
|
||||
- State 1 to 2 transition enqueues a single SyncML command so the outcome is stored and retried
|
||||
- State 2 checkins return ESP status inline in the SyncML response, not stored, so the 3 hour window does not
|
||||
explode the commands table
|
||||
- require_all_software splits into require_all_software_macos and require_all_software_windows; legacy name
|
||||
remains as a YAML/JSON alias via the existing renameto struct tag
|
||||
- ListMDMWindowsProfilesToInstall gets a hostUUID parameter mirroring the macOS helper
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Proposal: Windows setup experience, cancel if software fails
|
||||
|
||||
## Intent
|
||||
Windows hosts enrolling via Autopilot today land on the desktop as soon as enrollment completes, with no
|
||||
visible progress for profiles, setup software, or certificates, and no way for an admin to hold the user in
|
||||
OOBE until "work blocking" software is present. Introduce a Windows Enrollment Status Page (ESP) that tracks
|
||||
profile, SCEP, and setup-software progress during OOBE, and add a per-team `require_all_software_windows`
|
||||
switch that blocks the user with a "Try again" message when any critical item fails. This brings Windows to
|
||||
parity with the macOS `require_all_software` contract and gives IT admins the guarantees their organizations
|
||||
require.
|
||||
|
||||
## Scope
|
||||
- New per-team config field `require_all_software_windows` (default false) via REST API, YAML, and UI
|
||||
- Rename `require_all_software` to `require_all_software_macos` with a backward compatible alias
|
||||
- Detect Autopilot OOBE enrollments and mark hosts awaiting_configuration
|
||||
- Drive the Windows Enrollment Status Page via DMClient and EnrollmentStatusTracking CSPs
|
||||
- Finalize the ESP: release on success, block with "Try again" when require_all_software_windows=true and
|
||||
any item failed, or time out after 3 hours
|
||||
- Emit canceled_setup_experience activity on Windows cancellation
|
||||
|
||||
## Approach
|
||||
Three phases, each independently deployable. Phase 1 adds the config field and detects Autopilot OOBE at
|
||||
enrollment. Phase 2 initializes the ESP on the first qualifying Microsoft Management checkin and returns
|
||||
ongoing status inline on subsequent checkins. Phase 3 finalizes the flow when items reach terminal state or
|
||||
the 3 hour timeout fires. The existing setup_experience_status_results table and the
|
||||
CancelPendingSetupExperienceSteps helper are reused. Windows plugs into getManagementResponse as the single
|
||||
SyncML entry point.
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
# Delta for mdm-windows-setup-experience
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Windows require_all_software_windows config
|
||||
|
||||
Fleet SHALL expose a per-team boolean config field `require_all_software_windows`, defaulting to `false`,
|
||||
that controls whether the Windows setup experience blocks the end user when any setup-experience software
|
||||
fails to install.
|
||||
|
||||
#### Scenario: Default value on a new team
|
||||
|
||||
- GIVEN a newly created team with Windows MDM enabled
|
||||
- WHEN an admin reads the team's setup experience config
|
||||
- THEN `require_all_software_windows` is `false`
|
||||
|
||||
#### Scenario: Update via REST API
|
||||
|
||||
- GIVEN a team exists with Windows MDM enabled
|
||||
- WHEN an admin sends `PATCH /api/_version_/fleet/setup_experience` with
|
||||
`{"fleet_id": 1, "require_all_software_windows": true}`
|
||||
- THEN the field is persisted
|
||||
- AND subsequent reads return `true`
|
||||
|
||||
#### Scenario: Update via GitOps
|
||||
|
||||
- GIVEN a team YAML file sets `setup_experience.require_all_software_windows: true`
|
||||
- WHEN `fleetctl apply` processes the file
|
||||
- THEN the stored config reflects `true`
|
||||
- AND exporting the team back to YAML round-trips the value
|
||||
|
||||
#### Scenario: Rejected when Windows MDM is disabled
|
||||
|
||||
- GIVEN a team where Windows MDM is NOT enabled
|
||||
- WHEN an admin attempts to set `require_all_software_windows=true`
|
||||
- THEN the request is rejected with a validation error
|
||||
|
||||
### Requirement: Windows hosts awaiting configuration state
|
||||
|
||||
Fleet SHALL track whether each Windows-enrolled host is awaiting setup-experience completion using a
|
||||
tri-state column `awaiting_configuration` on `mdm_windows_enrollments` (0 = not awaiting, 1 = ESP not yet
|
||||
issued, 2 = ESP issued and in progress).
|
||||
|
||||
#### Scenario: Autopilot OOBE enrollment sets awaiting
|
||||
|
||||
- GIVEN a Windows host enrolls via Autopilot
|
||||
- AND the request includes an Azure JWT or WSTEP binary security token
|
||||
- AND the request indicates `NotInOobe=false`
|
||||
- WHEN Fleet stores the enrollment
|
||||
- THEN `awaiting_configuration=1`
|
||||
- AND `awaiting_configuration_at=NOW()`
|
||||
|
||||
#### Scenario: Programmatic fleetd enrollment does not set awaiting
|
||||
|
||||
- GIVEN a Windows host enrolls via fleetd using an orbit node key
|
||||
- WHEN Fleet stores the enrollment
|
||||
- THEN `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: Post-OOBE Autopilot re-enrollment clears awaiting
|
||||
|
||||
- GIVEN a Windows host that previously completed setup re-enrolls via Autopilot
|
||||
- AND the request indicates `NotInOobe=true`
|
||||
- WHEN Fleet upserts the enrollment row
|
||||
- THEN `awaiting_configuration=0`
|
||||
|
||||
### Requirement: Initial ESP command on first qualifying checkin
|
||||
|
||||
Fleet SHALL send an initial Windows ESP SyncML command covering profiles, SCEP certificates, and setup
|
||||
software on the first Microsoft Management checkin for a host with `awaiting_configuration=1` and a known
|
||||
`host_uuid`, and SHALL then transition the host to `awaiting_configuration=2`.
|
||||
|
||||
#### Scenario: Software defined, host recently orbit-enrolled
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=1` and orbit-enrolled 30 minutes ago
|
||||
- AND its team has setup-experience software items defined
|
||||
- WHEN the host sends a Microsoft Management checkin
|
||||
- THEN Fleet enqueues a SyncML command with DMClient ExpectedPolicies for each profile's top-most node
|
||||
- AND EnrollmentStatusTracking entries for each setup software item
|
||||
- AND SCEP entries for profiles containing SCEP certificates
|
||||
- AND DMClient `TimeoutUntilSyncFailure=3h`
|
||||
- AND the host row is updated to `awaiting_configuration=2`
|
||||
|
||||
#### Scenario: No software defined and orbit registered less than 10 minutes ago
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=1` and orbit-enrolled 3 minutes ago
|
||||
- AND its team has no setup-experience software items defined
|
||||
- WHEN the host sends a Microsoft Management checkin
|
||||
- THEN Fleet does nothing and leaves `awaiting_configuration=1`
|
||||
- AND no SyncML command is enqueued
|
||||
|
||||
#### Scenario: No software defined and orbit registered more than 10 minutes ago
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=1` and orbit-enrolled 15 minutes ago
|
||||
- AND its team has no setup-experience software items defined
|
||||
- WHEN the host sends a Microsoft Management checkin
|
||||
- THEN Fleet proceeds to the release path
|
||||
- AND `awaiting_configuration` is set to 0
|
||||
|
||||
### Requirement: Ongoing ESP status sync is inline, not stored
|
||||
|
||||
Fleet SHALL return current Windows ESP tracking status inline in the SyncML response body on every Microsoft
|
||||
Management checkin while `awaiting_configuration=2`, and SHALL NOT enqueue these updates as stored commands.
|
||||
|
||||
#### Scenario: Three consecutive checkins in state 2
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2`
|
||||
- WHEN the host makes three consecutive Microsoft Management checkins
|
||||
- THEN each response contains the current EnrollmentStatusTracking entries reflecting
|
||||
`setup_experience_status_results`
|
||||
- AND no new rows are written to the stored MDM commands table
|
||||
- AND dropping any one response leaves the next response self-sufficient
|
||||
|
||||
### Requirement: Terminal-state release and cancellation
|
||||
|
||||
Fleet SHALL finalize the Windows setup experience when the outcome is decided: release the user when the
|
||||
flow succeeded or when `require_all_software_windows=false` and all items are terminal; block the user with
|
||||
a "Try again" message when `require_all_software_windows=true` and any item failed or the flow timed out.
|
||||
|
||||
#### Scenario: All items succeed
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2`
|
||||
- AND all `setup_experience_status_results` rows are `success`
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet enqueues a final ESP command releasing the user to the desktop
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: require_all_software_windows=true with a failed install
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and its team has `require_all_software_windows=true`
|
||||
- AND at least one setup-experience software item is in `failure` state
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet cancels remaining pending items via `CancelPendingSetupExperienceSteps`
|
||||
- AND enqueues a final ESP command with `BlockInStatusPage=true`
|
||||
- AND `CustomErrorText="Critical software failed to install. Please try again. If this keeps happening,
|
||||
please contact your IT admin."`
|
||||
- AND emits a `canceled_setup_experience` activity citing the first failed software item by `display_name`
|
||||
(falling back to `name`)
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: require_all_software_windows=false with a failed install
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and its team has `require_all_software_windows=false`
|
||||
- AND at least one setup-experience software item is in `failure` state
|
||||
- AND all other items are terminal
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet enqueues a final ESP command releasing the user to the desktop
|
||||
- AND `CustomErrorText` surfaces the error to the user but does not block
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
### Requirement: 3 hour timeout
|
||||
|
||||
Fleet SHALL treat a host as timed out if `now - awaiting_configuration_at > 3 hours`, regardless of pending
|
||||
items.
|
||||
|
||||
#### Scenario: Timeout with require_all_software_windows=true
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and `awaiting_configuration_at` 3 hours 5 minutes ago
|
||||
- AND its team has `require_all_software_windows=true`
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet cancels remaining pending items
|
||||
- AND enqueues a blocking ESP command with `CustomErrorText`
|
||||
- AND emits a `canceled_setup_experience` activity
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: Timeout with require_all_software_windows=false
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and `awaiting_configuration_at` 3 hours 5 minutes ago
|
||||
- AND its team has `require_all_software_windows=false`
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet marks remaining items failed and enqueues a releasing ESP command
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
### Requirement: Windows checkbox on Setup experience > Install software
|
||||
|
||||
Fleet SHALL render a "Cancel setup if software fails" checkbox on the Windows tab of the Setup experience
|
||||
install-software page that reads and writes `require_all_software_windows` through the same
|
||||
`/api/_version_/fleet/setup_experience` endpoint used by macOS.
|
||||
|
||||
#### Scenario: Toggle the checkbox
|
||||
|
||||
- GIVEN an admin is on Setup experience > Install software, Windows tab
|
||||
- AND `require_all_software_windows=false`
|
||||
- WHEN the admin checks the box and clicks Save
|
||||
- THEN the UI calls `updateRequireAllSoftwareWindows(true)`
|
||||
- AND the admin sees a success state
|
||||
- AND reloading the page shows the box checked
|
||||
|
||||
### Requirement: canceled_setup_experience activity extended to Windows
|
||||
|
||||
Fleet SHALL emit the existing `canceled_setup_experience` activity type for Windows cancellations with the
|
||||
same schema used for macOS, naming the first failed software item using `display_name` with a fallback to
|
||||
`name`.
|
||||
|
||||
#### Scenario: Windows cancellation produces an activity
|
||||
|
||||
- GIVEN a Windows host setup experience is canceled due to a failed install of "Microsoft Office"
|
||||
- WHEN Fleet finalizes the cancellation
|
||||
- THEN an activity of type `canceled_setup_experience` is recorded
|
||||
- AND the payload references "Microsoft Office" via `display_name`
|
||||
- AND the activity is visible in both the global activity feed and the host's activity feed
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: require_all_software field rename
|
||||
|
||||
Fleet SHALL treat the existing `require_all_software` config field as an alias for
|
||||
`require_all_software_macos`. Previously, `require_all_software` was the authoritative macOS-only field.
|
||||
|
||||
#### Scenario: Legacy YAML still applies
|
||||
|
||||
- GIVEN a GitOps YAML file sets `macos_setup.require_all_software: true` (legacy form)
|
||||
- WHEN `fleetctl apply` processes the file
|
||||
- THEN the stored value for `require_all_software_macos` is `true`
|
||||
- AND no deprecation error is raised in this release
|
||||
|
||||
#### Scenario: Exported YAML uses the new name
|
||||
|
||||
- GIVEN a team has `require_all_software_macos=true`
|
||||
- WHEN the team is exported to YAML via GitOps
|
||||
- THEN the exported file contains `setup_experience.require_all_software_macos: true`
|
||||
- AND not the legacy `macos_setup.require_all_software` form
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
_(None. The legacy `require_all_software` field remains readable; it is renamed, not removed.)_
|
||||
103
openspec/changes/windows-setup-cancel-if-software-fails/tasks.md
Normal file
103
openspec/changes/windows-setup-cancel-if-software-fails/tasks.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Tasks
|
||||
|
||||
## 1. Database migration
|
||||
- [ ] 1.1 Add awaiting_configuration TINYINT NOT NULL DEFAULT 0 and awaiting_configuration_at DATETIME(6)
|
||||
NULL to mdm_windows_enrollments
|
||||
- [ ] 1.2 Update server/datastore/mysql/schema.sql
|
||||
- [ ] 1.3 Migration test covering default values on existing rows
|
||||
|
||||
## 2. Config struct and rename
|
||||
- [ ] 2.1 Rename MacOSSetup.RequireAllSoftware to RequireAllSoftwareMacOS
|
||||
- [ ] 2.2 Add RequireAllSoftwareWindows *bool to the setup experience struct
|
||||
- [ ] 2.3 Add renameto tag so require_all_software deserializes to the macOS field
|
||||
- [ ] 2.4 Support both macos_setup and setup_experience YAML parent keys for one release
|
||||
|
||||
## 3. REST API and GitOps
|
||||
- [ ] 3.1 Extend MDMAppleSetupPayload with RequireAllSoftwareWindows *bool
|
||||
- [ ] 3.2 Wire the EE handler in ee/server/service/mdm.go to persist it
|
||||
- [ ] 3.3 Team config apply path in ee/server/service/teams.go handles the new field
|
||||
- [ ] 3.4 Validate that Windows MDM is enabled before accepting the field
|
||||
- [ ] 3.5 Update cmd/fleetctl/fleetctl/testdata/ golden files
|
||||
- [ ] 3.6 Document the field in docs/REST API/rest-api.md
|
||||
|
||||
## 4. Detection at enrollment
|
||||
- [ ] 4.1 Classify the enrollment source in storeWindowsMDMEnrolledDevice using the binary security token type
|
||||
- [ ] 4.2 Set awaiting_configuration=1 and awaiting_configuration_at=NOW() when source is Autopilot (JWT or
|
||||
WSTEP) and NotInOobe=false
|
||||
- [ ] 4.3 Set awaiting_configuration=0 otherwise
|
||||
- [ ] 4.4 Verify ON DUPLICATE KEY UPDATE correctly updates the columns on re-enrollment
|
||||
- [ ] 4.5 Integration test: fleetd programmatic enroll leaves awaiting_configuration=0
|
||||
- [ ] 4.6 Integration test: simulated Autopilot JWT enroll with NotInOobe=false sets awaiting_configuration=1
|
||||
- [ ] 4.7 Integration test: Autopilot re-enroll post-OOBE resets to 0
|
||||
|
||||
## 5. Frontend config plumbing
|
||||
- [ ] 5.1 Add field to frontend/interfaces/config.ts and frontend/interfaces/team.ts
|
||||
- [ ] 5.2 Add field to frontend/__mocks__/configMock.ts
|
||||
- [ ] 5.3 Add mdmAPI.updateRequireAllSoftwareWindows in frontend/services/entities/mdm.ts
|
||||
|
||||
## 6. DMClient and EnrollmentStatusTracking SyncML helpers
|
||||
- [ ] 6.1 Add DMClient CSP path constants for ExpectedPolicies, TimeoutUntilSyncFailure, CustomErrorText, and
|
||||
BlockInStatusPage in server/mdm/microsoft/syncml/syncml.go
|
||||
- [ ] 6.2 Add EnrollmentStatusTracking CSP helpers for software tracking entries
|
||||
- [ ] 6.3 Add SCEP certificate ESP node helpers
|
||||
- [ ] 6.4 Unit tests for each helper with golden SyncML XML assertions
|
||||
|
||||
## 7. Single-host profile listing
|
||||
- [ ] 7.1 Add hostUUID string parameter to ListMDMWindowsProfilesToInstall mirroring the macOS helper
|
||||
- [ ] 7.2 Update existing callers to pass empty string
|
||||
- [ ] 7.3 Test for single-host filtering
|
||||
|
||||
## 8. Initial ESP command generation
|
||||
- [ ] 8.1 On getManagementResponse with awaiting_configuration=1 and host_uuid set, query setup software for
|
||||
the host's team
|
||||
- [ ] 8.2 If no items and orbit-enrolled less than 10 minutes ago, do nothing
|
||||
- [ ] 8.3 If no items and at least 10 minutes, proceed to release path
|
||||
- [ ] 8.4 If items exist, fetch applicable profiles and current setup_experience_status_results
|
||||
- [ ] 8.5 Build initial SyncML command: profile top-most nodes, SCEP nodes, software tracking entries
|
||||
- [ ] 8.6 Set TimeoutUntilSyncFailure to 3 hours
|
||||
- [ ] 8.7 Enqueue as stored command; transition row to awaiting_configuration=2
|
||||
- [ ] 8.8 Integration test: first checkin after orbit registers produces the expected SyncML
|
||||
|
||||
## 9. Ongoing status sync
|
||||
- [ ] 9.1 On getManagementResponse with awaiting_configuration=2, fetch current setup_experience_status_results
|
||||
- [ ] 9.2 Build EnrollmentStatusTracking entries reflecting current state of each item
|
||||
- [ ] 9.3 Return inline in SyncML response; do not enqueue
|
||||
- [ ] 9.4 Integration test: three consecutive checkins return consistent status with no duplicate stored commands
|
||||
|
||||
## 10. Frontend checkbox
|
||||
- [ ] 10.1 Add checkbox block for platform windows in InstallSoftwareForm.tsx at the existing macOS block
|
||||
- [ ] 10.2 Add requireAllSoftwareWindows state and handler
|
||||
- [ ] 10.3 Pass savedRequireAllSoftwareWindows prop from parent
|
||||
- [ ] 10.4 Extend shouldUpdateRequireAll to trigger for Windows
|
||||
- [ ] 10.5 Wire onClickSave to call updateRequireAllSoftwareWindows
|
||||
- [ ] 10.6 Update macOS checkbox label and tooltip to match the new Windows copy
|
||||
- [ ] 10.7 Verify canceled_setup_experience appears in the activity filter dropdown and feed
|
||||
- [ ] 10.8 Test coverage for the new checkbox
|
||||
|
||||
## 11. Timeout path
|
||||
- [ ] 11.1 Compute age = now - awaiting_configuration_at in getManagementResponse state 2
|
||||
- [ ] 11.2 If age > 3h, call CancelPendingSetupExperienceSteps
|
||||
- [ ] 11.3 If require_all_software_windows=true, enqueue final ESP with BlockInStatusPage=true and
|
||||
CustomErrorText
|
||||
- [ ] 11.4 If require_all_software_windows=false, enqueue final ESP marking remaining items failed but
|
||||
allowing the user through
|
||||
- [ ] 11.5 Emit canceled_setup_experience activity using display_name with fallback to name for the first
|
||||
failed software item
|
||||
- [ ] 11.6 Set awaiting_configuration=0
|
||||
|
||||
## 12. Blocking failure path
|
||||
- [ ] 12.1 If require_all_software_windows=true and any profile or software is in failure state, cancel
|
||||
remaining, emit activity, enqueue blocking ESP, set awaiting_configuration=0
|
||||
|
||||
## 13. Release path
|
||||
- [ ] 13.1 If all items terminal, enqueue final ESP; include CustomErrorText if any errors exist; set
|
||||
awaiting_configuration=0
|
||||
|
||||
## 14. macOS activity text update
|
||||
- [ ] 14.1 Add "End user was asked to restart" to macOS canceled_setup_experience activity text
|
||||
|
||||
## 15. Docs and QA
|
||||
- [ ] 15.1 Update docs/Using Fleet/Windows & Linux setup experience.md with SHIFT+F10 breakglass and remote
|
||||
fleet script instructions
|
||||
- [ ] 15.2 Load test: verify no additional steady-state load from the new columns and checkin paths
|
||||
- [ ] 15.3 Execute all test plan items from issue #38785
|
||||
218
openspec/specs/mdm-windows-setup-experience/spec.md
Normal file
218
openspec/specs/mdm-windows-setup-experience/spec.md
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
# mdm-windows-setup-experience
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Windows require_all_software_windows config
|
||||
|
||||
Fleet SHALL expose a per-team boolean config field `require_all_software_windows`, defaulting to `false`,
|
||||
that controls whether the Windows setup experience blocks the end user when any setup-experience software
|
||||
fails to install.
|
||||
|
||||
#### Scenario: Default value on a new team
|
||||
|
||||
- GIVEN a newly created team with Windows MDM enabled
|
||||
- WHEN an admin reads the team's setup experience config
|
||||
- THEN `require_all_software_windows` is `false`
|
||||
|
||||
#### Scenario: Update via REST API
|
||||
|
||||
- GIVEN a team exists with Windows MDM enabled
|
||||
- WHEN an admin sends `PATCH /api/_version_/fleet/setup_experience` with
|
||||
`{"fleet_id": 1, "require_all_software_windows": true}`
|
||||
- THEN the field is persisted
|
||||
- AND subsequent reads return `true`
|
||||
|
||||
#### Scenario: Update via GitOps
|
||||
|
||||
- GIVEN a team YAML file sets `setup_experience.require_all_software_windows: true`
|
||||
- WHEN `fleetctl apply` processes the file
|
||||
- THEN the stored config reflects `true`
|
||||
- AND exporting the team back to YAML round-trips the value
|
||||
|
||||
#### Scenario: Rejected when Windows MDM is disabled
|
||||
|
||||
- GIVEN a team where Windows MDM is NOT enabled
|
||||
- WHEN an admin attempts to set `require_all_software_windows=true`
|
||||
- THEN the request is rejected with a validation error
|
||||
|
||||
### Requirement: Windows hosts awaiting configuration state
|
||||
|
||||
Fleet SHALL track whether each Windows-enrolled host is awaiting setup-experience completion using a
|
||||
tri-state column `awaiting_configuration` on `mdm_windows_enrollments` (0 = not awaiting, 1 = ESP not yet
|
||||
issued, 2 = ESP issued and in progress).
|
||||
|
||||
#### Scenario: Autopilot OOBE enrollment sets awaiting
|
||||
|
||||
- GIVEN a Windows host enrolls via Autopilot
|
||||
- AND the request includes an Azure JWT or WSTEP binary security token
|
||||
- AND the request indicates `NotInOobe=false`
|
||||
- WHEN Fleet stores the enrollment
|
||||
- THEN `awaiting_configuration=1`
|
||||
- AND `awaiting_configuration_at=NOW()`
|
||||
|
||||
#### Scenario: Programmatic fleetd enrollment does not set awaiting
|
||||
|
||||
- GIVEN a Windows host enrolls via fleetd using an orbit node key
|
||||
- WHEN Fleet stores the enrollment
|
||||
- THEN `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: Post-OOBE Autopilot re-enrollment clears awaiting
|
||||
|
||||
- GIVEN a Windows host that previously completed setup re-enrolls via Autopilot
|
||||
- AND the request indicates `NotInOobe=true`
|
||||
- WHEN Fleet upserts the enrollment row
|
||||
- THEN `awaiting_configuration=0`
|
||||
|
||||
### Requirement: Initial ESP command on first qualifying checkin
|
||||
|
||||
Fleet SHALL send an initial Windows ESP SyncML command covering profiles, SCEP certificates, and setup
|
||||
software on the first Microsoft Management checkin for a host with `awaiting_configuration=1` and a known
|
||||
`host_uuid`, and SHALL then transition the host to `awaiting_configuration=2`.
|
||||
|
||||
#### Scenario: Software defined, host recently orbit-enrolled
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=1` and orbit-enrolled 30 minutes ago
|
||||
- AND its team has setup-experience software items defined
|
||||
- WHEN the host sends a Microsoft Management checkin
|
||||
- THEN Fleet enqueues a SyncML command with DMClient ExpectedPolicies for each profile's top-most node
|
||||
- AND EnrollmentStatusTracking entries for each setup software item
|
||||
- AND SCEP entries for profiles containing SCEP certificates
|
||||
- AND DMClient `TimeoutUntilSyncFailure=3h`
|
||||
- AND the host row is updated to `awaiting_configuration=2`
|
||||
|
||||
#### Scenario: No software defined and orbit registered less than 10 minutes ago
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=1` and orbit-enrolled 3 minutes ago
|
||||
- AND its team has no setup-experience software items defined
|
||||
- WHEN the host sends a Microsoft Management checkin
|
||||
- THEN Fleet does nothing and leaves `awaiting_configuration=1`
|
||||
- AND no SyncML command is enqueued
|
||||
|
||||
#### Scenario: No software defined and orbit registered more than 10 minutes ago
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=1` and orbit-enrolled 15 minutes ago
|
||||
- AND its team has no setup-experience software items defined
|
||||
- WHEN the host sends a Microsoft Management checkin
|
||||
- THEN Fleet proceeds to the release path
|
||||
- AND `awaiting_configuration` is set to 0
|
||||
|
||||
### Requirement: Ongoing ESP status sync is inline, not stored
|
||||
|
||||
Fleet SHALL return current Windows ESP tracking status inline in the SyncML response body on every Microsoft
|
||||
Management checkin while `awaiting_configuration=2`, and SHALL NOT enqueue these updates as stored commands.
|
||||
|
||||
#### Scenario: Three consecutive checkins in state 2
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2`
|
||||
- WHEN the host makes three consecutive Microsoft Management checkins
|
||||
- THEN each response contains the current EnrollmentStatusTracking entries reflecting
|
||||
`setup_experience_status_results`
|
||||
- AND no new rows are written to the stored MDM commands table
|
||||
- AND dropping any one response leaves the next response self-sufficient
|
||||
|
||||
### Requirement: Terminal-state release and cancellation
|
||||
|
||||
Fleet SHALL finalize the Windows setup experience when the outcome is decided: release the user when the
|
||||
flow succeeded or when `require_all_software_windows=false` and all items are terminal; block the user with
|
||||
a "Try again" message when `require_all_software_windows=true` and any item failed or the flow timed out.
|
||||
|
||||
#### Scenario: All items succeed
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2`
|
||||
- AND all `setup_experience_status_results` rows are `success`
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet enqueues a final ESP command releasing the user to the desktop
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: require_all_software_windows=true with a failed install
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and its team has `require_all_software_windows=true`
|
||||
- AND at least one setup-experience software item is in `failure` state
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet cancels remaining pending items via `CancelPendingSetupExperienceSteps`
|
||||
- AND enqueues a final ESP command with `BlockInStatusPage=true`
|
||||
- AND `CustomErrorText="Critical software failed to install. Please try again. If this keeps happening,
|
||||
please contact your IT admin."`
|
||||
- AND emits a `canceled_setup_experience` activity citing the first failed software item by `display_name`
|
||||
(falling back to `name`)
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: require_all_software_windows=false with a failed install
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and its team has `require_all_software_windows=false`
|
||||
- AND at least one setup-experience software item is in `failure` state
|
||||
- AND all other items are terminal
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet enqueues a final ESP command releasing the user to the desktop
|
||||
- AND `CustomErrorText` surfaces the error to the user but does not block
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
### Requirement: 3 hour timeout
|
||||
|
||||
Fleet SHALL treat a host as timed out if `now - awaiting_configuration_at > 3 hours`, regardless of pending
|
||||
items.
|
||||
|
||||
#### Scenario: Timeout with require_all_software_windows=true
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and `awaiting_configuration_at` 3 hours 5 minutes ago
|
||||
- AND its team has `require_all_software_windows=true`
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet cancels remaining pending items
|
||||
- AND enqueues a blocking ESP command with `CustomErrorText`
|
||||
- AND emits a `canceled_setup_experience` activity
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
#### Scenario: Timeout with require_all_software_windows=false
|
||||
|
||||
- GIVEN a host has `awaiting_configuration=2` and `awaiting_configuration_at` 3 hours 5 minutes ago
|
||||
- AND its team has `require_all_software_windows=false`
|
||||
- WHEN the host makes a Microsoft Management checkin
|
||||
- THEN Fleet marks remaining items failed and enqueues a releasing ESP command
|
||||
- AND `awaiting_configuration=0`
|
||||
|
||||
### Requirement: Windows checkbox on Setup experience > Install software
|
||||
|
||||
Fleet SHALL render a "Cancel setup if software fails" checkbox on the Windows tab of the Setup experience
|
||||
install-software page that reads and writes `require_all_software_windows` through the same
|
||||
`/api/_version_/fleet/setup_experience` endpoint used by macOS.
|
||||
|
||||
#### Scenario: Toggle the checkbox
|
||||
|
||||
- GIVEN an admin is on Setup experience > Install software, Windows tab
|
||||
- AND `require_all_software_windows=false`
|
||||
- WHEN the admin checks the box and clicks Save
|
||||
- THEN the UI calls `updateRequireAllSoftwareWindows(true)`
|
||||
- AND the admin sees a success state
|
||||
- AND reloading the page shows the box checked
|
||||
|
||||
### Requirement: canceled_setup_experience activity covers Windows
|
||||
|
||||
Fleet SHALL emit the `canceled_setup_experience` activity type for Windows cancellations with the same
|
||||
schema used for macOS, naming the first failed software item using `display_name` with a fallback to `name`.
|
||||
|
||||
#### Scenario: Windows cancellation produces an activity
|
||||
|
||||
- GIVEN a Windows host setup experience is canceled due to a failed install of "Microsoft Office"
|
||||
- WHEN Fleet finalizes the cancellation
|
||||
- THEN an activity of type `canceled_setup_experience` is recorded
|
||||
- AND the payload references "Microsoft Office" via `display_name`
|
||||
- AND the activity is visible in both the global activity feed and the host's activity feed
|
||||
|
||||
### Requirement: require_all_software field naming and backward compatibility
|
||||
|
||||
Fleet SHALL expose the macOS variant of the setting as `require_all_software_macos` and SHALL continue to
|
||||
accept the legacy name `require_all_software` as a YAML/JSON alias for `require_all_software_macos`. Exported
|
||||
configurations SHALL use the new name under `setup_experience`.
|
||||
|
||||
#### Scenario: Legacy YAML still applies
|
||||
|
||||
- GIVEN a GitOps YAML file sets `macos_setup.require_all_software: true` (legacy form)
|
||||
- WHEN `fleetctl apply` processes the file
|
||||
- THEN the stored value for `require_all_software_macos` is `true`
|
||||
|
||||
#### Scenario: Exported YAML uses the new name
|
||||
|
||||
- GIVEN a team has `require_all_software_macos=true`
|
||||
- WHEN the team is exported to YAML via GitOps
|
||||
- THEN the exported file contains `setup_experience.require_all_software_macos: true`
|
||||
- AND not the legacy `macos_setup.require_all_software` form
|
||||
Loading…
Reference in a new issue