Add require_all_software_windows config option (#43011)

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

This PR simply adds the `require_all_software_windows` config option. It
doesn't use it. The logic to use it will be hooked up in subsequent PRs.

The fleetctl TestIntegrationsPreview test is expected to fail since it
builds the server against main and doesn't know about our new config
option.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

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

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

## New Fleet configuration settings

- [x] Verified that the setting is exported via `fleetctl
generate-gitops`
- Not exported. generate-gitops does not export
require_all_software_windows (or require_all_software_macos either). The
generateControls function (generate_gitops.go) outputs a "TODO: update
with your setup_experience configuration" placeholder when any setup
experience config exists, rather than exporting individual field values.
This is a pre-existing limitation that applies equally to both fields -
not something introduced by our PR.
- [x] Verified the setting is documented in a separate PR to [the GitOps
documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485)
- Yes. PR #42046 adds require_all_software_windows to both docs/REST
API/rest-api.md and docs/Configuration/yaml-files.md.
- [x] Verified that the setting is cleared on the server if it is not
supplied in a YAML file (or that it is documented as being optional)
- Yes, it gets cleared to false - both when setup_experience: is present
without the field, and when setup_experience: is omitted entirely. This
is the same behavior as the existing require_all_software_macos field
- [x] Verified that any relevant UI is disabled when GitOps mode is
enabled
- Covered by #42854 (frontend subtask). The existing macOS checkbox in
InstallSoftwareForm.tsx:271 already checks gitOpsModeEnabled to disable
itself. The Windows checkbox to be added in #42854 will follow the same
pattern.


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

## Summary by CodeRabbit

* **New Features**
* Added a Windows setup experience software requirement setting. When
enabled, Windows devices will cancel the Autopilot setup if any required
software installation fails.

* **Tests**
* Added test coverage for the new Windows software requirement
configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Victor Lyuboslavsky 2026-04-06 17:39:59 -05:00 committed by GitHub
parent b1ea2121da
commit c4479c6a84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 198 additions and 17 deletions

View file

@ -0,0 +1 @@
- Added `require_all_software_windows` setting to cancel the Windows setup experience if any software install fails during Autopilot enrollment, matching the existing macOS behavior.

View file

@ -2703,6 +2703,39 @@ spec:
_, err = RunAppNoChecks([]string{"apply", "-f", name})
require.ErrorContains(t, err, `The profile can't include "await_device_configured" option.`)
assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
})
t.Run("require_all_software_windows", func(t *testing.T) {
ds := setupServer(t, true)
// Enable Windows MDM in the app config.
mockStore.Lock()
mockStore.appConfig.MDM.WindowsEnabledAndConfigured = true
mockStore.Unlock()
b, err := os.ReadFile(filepath.Join("testdata", "macosSetupExpectedTeam1Set.yml"))
require.NoError(t, err)
expectedTm1 := fmt.Sprintf(string(b), "", "", "", "")
// Apply team with require_all_software_windows enabled.
windowsRequireSpec := `
apiVersion: v1
kind: fleet
spec:
team:
name: tm1
mdm:
setup_experience:
require_all_software_windows: true
`
name := writeTmpYml(t, windowsRequireSpec)
assert.Equal(t, "[+] applied 1 fleet\n", RunAppForTest(t, []string{"apply", "-f", name}))
assert.True(t, ds.SaveTeamFuncInvoked)
// Verify the output includes require_all_software_windows: true.
expectedWithWindowsRequire := strings.ReplaceAll(expectedTm1, `require_all_software_windows: false`, `require_all_software_windows: true`)
assert.YAMLEq(t, expectedWithWindowsRequire, RunAppForTest(t, []string{"get", "teams", "--yaml"}))
})
t.Run("new bootstrap package", func(t *testing.T) {

View file

@ -162,7 +162,8 @@
"script": null,
"software": null,
"manual_agent_install": null,
"require_all_software_macos": false
"require_all_software_macos": false,
"require_all_software_windows": false
},
"setup_experience": {
"macos_bootstrap_package": null,
@ -173,7 +174,8 @@
"macos_script": null,
"software": null,
"macos_manual_agent_install": null,
"require_all_software_macos": false
"require_all_software_macos": false,
"require_all_software_windows": false
},
"windows_settings": {
"custom_settings": null,

View file

@ -134,7 +134,8 @@
"script": null,
"software": null,
"manual_agent_install": null,
"require_all_software_macos": false
"require_all_software_macos": false,
"require_all_software_windows": false
},
"setup_experience": {
"macos_bootstrap_package": null,
@ -145,7 +146,8 @@
"macos_script": null,
"software": null,
"macos_manual_agent_install": null,
"require_all_software_macos": false
"require_all_software_macos": false,
"require_all_software_windows": false
},
"windows_settings": {
"custom_settings": null,

View file

@ -74,6 +74,7 @@ spec:
macos_setup_assistant:
manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
script:
software:
setup_experience:
@ -84,6 +85,7 @@ spec:
apple_setup_assistant:
macos_manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
macos_script:
software:
windows_settings:

View file

@ -74,6 +74,7 @@ spec:
macos_setup_assistant:
manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
script:
software:
setup_experience:
@ -84,6 +85,7 @@ spec:
apple_setup_assistant:
macos_manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
macos_script:
software:
windows_settings:

View file

@ -112,7 +112,8 @@
"script": null,
"software": null,
"manual_agent_install": null,
"require_all_software_macos": false
"require_all_software_macos": false,
"require_all_software_windows": false
},
"setup_experience": {
"macos_bootstrap_package": null,
@ -123,7 +124,8 @@
"macos_script": null,
"software": null,
"macos_manual_agent_install": null,
"require_all_software_macos": false
"require_all_software_macos": false,
"require_all_software_windows": false
},
"windows_settings": {
"custom_settings": null,

View file

@ -74,6 +74,7 @@ spec:
macos_setup_assistant:
manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
script:
software:
setup_experience:
@ -84,6 +85,7 @@ spec:
apple_setup_assistant:
macos_manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
macos_script:
software:
windows_settings:

View file

@ -55,6 +55,7 @@
"macos_manual_agent_install": null,
"macos_script": null,
"require_all_software_macos": false,
"require_all_software_windows": false,
"software": null
},
"windows_require_bitlocker_pin": false,
@ -126,6 +127,7 @@
"macos_setup_assistant": null,
"manual_agent_install": null,
"require_all_software_macos": false,
"require_all_software_windows": false,
"script": null,
"software": null
},
@ -230,6 +232,7 @@
"macos_manual_agent_install": null,
"macos_script": null,
"require_all_software_macos": false,
"require_all_software_windows": false,
"software": null
},
"windows_require_bitlocker_pin": false,
@ -316,6 +319,7 @@
"macos_setup_assistant": null,
"manual_agent_install": null,
"require_all_software_macos": false,
"require_all_software_windows": false,
"script": null,
"software": null
},

View file

@ -46,6 +46,7 @@ spec:
apple_setup_assistant:
macos_manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
macos_script:
software:
scripts: null
@ -98,6 +99,7 @@ spec:
macos_setup_assistant:
manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
script:
software:
scripts: null
@ -157,6 +159,7 @@ spec:
apple_setup_assistant:
macos_manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
macos_script:
software:
scripts: null
@ -210,6 +213,7 @@ spec:
macos_setup_assistant:
manual_agent_install:
require_all_software_macos: false
require_all_software_windows: false
script:
software:
scripts: null

View file

@ -65,6 +65,7 @@ spec:
macos_setup_assistant: null
manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
enable_release_device_manually: false
script: null
software: null
@ -75,6 +76,7 @@ spec:
apple_setup_assistant: null
macos_manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
apple_enable_release_device_manually: false
macos_script: null
software: null

View file

@ -65,6 +65,7 @@ spec:
macos_setup_assistant: %s
manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
enable_release_device_manually: false
script: null
software: null
@ -75,6 +76,7 @@ spec:
apple_setup_assistant: %s
macos_manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
apple_enable_release_device_manually: false
macos_script: null
software: null

View file

@ -30,6 +30,7 @@ spec:
apple_setup_assistant: null
macos_manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
apple_enable_release_device_manually: false
macos_script: null
software: null
@ -82,6 +83,7 @@ spec:
macos_setup_assistant: null
manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
enable_release_device_manually: false
script: null
software: null

View file

@ -31,6 +31,7 @@ spec:
macos_manual_agent_install: null
apple_enable_release_device_manually: false
require_all_software_macos: false
require_all_software_windows: false
macos_script: null
software: null
macos_updates:
@ -83,6 +84,7 @@ spec:
manual_agent_install: null
enable_release_device_manually: false
require_all_software_macos: false
require_all_software_windows: false
script: null
software: null
macos_updates:

View file

@ -28,6 +28,7 @@ spec:
apple_setup_assistant: null
macos_manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
apple_enable_release_device_manually: false
macos_script: null
software: null
@ -80,6 +81,7 @@ spec:
macos_setup_assistant: null
manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
enable_release_device_manually: false
script: null
software: null

View file

@ -29,6 +29,7 @@ spec:
apple_setup_assistant: %s
macos_manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
apple_enable_release_device_manually: false
macos_script: null
software: null
@ -81,6 +82,7 @@ spec:
macos_setup_assistant: %s
manual_agent_install: null
require_all_software_macos: false
require_all_software_windows: false
enable_release_device_manually: false
script: null
software: null

View file

@ -233,6 +233,11 @@ func (svc *Service) updateAppConfigMDMAppleSetup(ctx context.Context, payload fl
didUpdate = true
}
if payload.RequireAllSoftwareWindows != nil && ac.MDM.MacOSSetup.RequireAllSoftwareWindows != *payload.RequireAllSoftwareWindows {
ac.MDM.MacOSSetup.RequireAllSoftwareWindows = *payload.RequireAllSoftwareWindows
didUpdate = true
}
if payload.EnableReleaseDeviceManually != nil {
if ac.MDM.MacOSSetup.EnableReleaseDeviceManually.Value != *payload.EnableReleaseDeviceManually {
ac.MDM.MacOSSetup.EnableReleaseDeviceManually = optjson.SetBool(*payload.EnableReleaseDeviceManually)
@ -311,6 +316,10 @@ func (svc *Service) validateMDMAppleSetupPayload(ctx context.Context, payload fl
return fleet.ErrMDMNotConfigured
}
if payload.RequireAllSoftwareWindows != nil && *payload.RequireAllSoftwareWindows && !ac.MDM.WindowsEnabledAndConfigured {
return fleet.ErrWindowsMDMNotConfigured
}
if payload.EnableEndUserAuthentication != nil && *payload.EnableEndUserAuthentication {
if ac.MDM.EndUserAuthentication.IsEmpty() {
// TODO: update this error message to include steps to resolve the issue once docs for IdP

View file

@ -1267,6 +1267,11 @@ func (svc *Service) createTeamFromSpec(
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("setup_experience.lock_end_user_info", `"enable_end_user_authentication" must be set to "true" in order to enable "lock_end_user_info"`))
}
if macOSSetup.RequireAllSoftwareWindows && !appCfg.MDM.WindowsEnabledAndConfigured {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("setup_experience.require_all_software_windows",
`Couldn't update setup_experience.require_all_software_windows. `+fleet.ErrWindowsMDMNotConfigured.Error()))
}
// Default the value of "lock_end_user_info" to the value of "enable_end_user_authentication" if not explicitly set in the spec to keep prior
// behavior.
if !macOSSetup.LockEndUserInfo.Valid {
@ -1593,10 +1598,19 @@ func (svc *Service) editTeamFromSpec(
}
team.Config.MDM.MacOSSetup.RequireAllSoftware = spec.MDM.MacOSSetup.RequireAllSoftware
didUpdateWindowsRequireAllSoftware := spec.MDM.MacOSSetup.RequireAllSoftwareWindows != oldMacOSSetup.RequireAllSoftwareWindows
windowsEnabledAndConfigured := appCfg.MDM.WindowsEnabledAndConfigured
if opts.DryRunAssumptions != nil && opts.DryRunAssumptions.WindowsEnabledAndConfigured.Valid {
windowsEnabledAndConfigured = opts.DryRunAssumptions.WindowsEnabledAndConfigured.Value
}
if didUpdateWindowsRequireAllSoftware && spec.MDM.MacOSSetup.RequireAllSoftwareWindows {
if !windowsEnabledAndConfigured {
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("setup_experience.require_all_software_windows",
`Couldn't update setup_experience.require_all_software_windows. `+fleet.ErrWindowsMDMNotConfigured.Error()))
}
}
team.Config.MDM.MacOSSetup.RequireAllSoftwareWindows = spec.MDM.MacOSSetup.RequireAllSoftwareWindows
if spec.MDM.WindowsSettings.CustomSettings.Set {
if !windowsEnabledAndConfigured &&
len(spec.MDM.WindowsSettings.CustomSettings.Value) > 0 &&
@ -2036,6 +2050,11 @@ func (svc *Service) updateTeamMDMAppleSetup(ctx context.Context, tm *fleet.Team,
didUpdate = true
}
if payload.RequireAllSoftwareWindows != nil && tm.Config.MDM.MacOSSetup.RequireAllSoftwareWindows != *payload.RequireAllSoftwareWindows {
tm.Config.MDM.MacOSSetup.RequireAllSoftwareWindows = *payload.RequireAllSoftwareWindows
didUpdate = true
}
if payload.ManualAgentInstall != nil {
if tm.Config.MDM.MacOSSetup.ManualAgentInstall.Value != *payload.ManualAgentInstall {
// Try to load the bootstrap package to verify it exists.

View file

@ -221,7 +221,7 @@ CREATE TABLE `app_config_json` (
PRIMARY KEY (`id`)
) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null, \"update_new_hosts\": null}, \"macos_setup\": {\"script\": null, \"software\": null, \"bootstrap_package\": null, \"lock_end_user_info\": false, \"manual_agent_install\": null, \"macos_setup_assistant\": null, \"require_all_software_macos\": false, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null, \"update_new_hosts\": false}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null, \"update_new_hosts\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"android_settings\": {\"certificates\": null, \"custom_settings\": null}, \"apple_server_url\": \"\", \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_terms_expired\": false, \"apple_business_manager\": null, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_entra_tenant_ids\": [], \"volume_purchasing_program\": null, \"windows_migration_enabled\": false, \"enable_recovery_lock_password\": false, \"windows_require_bitlocker_pin\": null, \"android_enabled_and_configured\": false, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false, \"apple_require_hardware_attestation\": false, \"enable_turn_on_windows_mdm_manually\": false}, \"gitops\": {\"exceptions\": {\"labels\": true, \"secrets\": true, \"software\": false}, \"repository_url\": \"\", \"gitops_mode_enabled\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null, \"conditional_access_enabled\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"sso_server_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\", \"alternative_browser_host\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null, \"update_new_hosts\": null}, \"macos_setup\": {\"script\": null, \"software\": null, \"bootstrap_package\": null, \"lock_end_user_info\": false, \"manual_agent_install\": null, \"macos_setup_assistant\": null, \"require_all_software_macos\": false, \"require_all_software_windows\": false, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null, \"update_new_hosts\": false}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null, \"update_new_hosts\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"android_settings\": {\"certificates\": null, \"custom_settings\": null}, \"apple_server_url\": \"\", \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_terms_expired\": false, \"apple_business_manager\": null, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_entra_tenant_ids\": [], \"volume_purchasing_program\": null, \"windows_migration_enabled\": false, \"enable_recovery_lock_password\": false, \"windows_require_bitlocker_pin\": null, \"android_enabled_and_configured\": false, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false, \"apple_require_hardware_attestation\": false, \"enable_turn_on_windows_mdm_manually\": false}, \"gitops\": {\"exceptions\": {\"labels\": true, \"secrets\": true, \"software\": false}, \"repository_url\": \"\", \"gitops_mode_enabled\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null, \"conditional_access_enabled\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"sso_server_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\", \"alternative_browser_host\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `batch_activities` (

View file

@ -552,6 +552,7 @@ type MacOSSetup struct {
Software optjson.Slice[*MacOSSetupSoftware] `json:"software"`
ManualAgentInstall optjson.Bool `json:"manual_agent_install" renameto:"macos_manual_agent_install"`
RequireAllSoftware bool `json:"require_all_software_macos"`
RequireAllSoftwareWindows bool `json:"require_all_software_windows"`
}
func (mos *MacOSSetup) Validate() error {

View file

@ -526,6 +526,7 @@ type MDMAppleSetupPayload struct {
EnableReleaseDeviceManually *bool `json:"enable_release_device_manually" renameto:"apple_enable_release_device_manually"`
ManualAgentInstall *bool `json:"manual_agent_install" renameto:"macos_manual_agent_install"`
RequireAllSoftware *bool `json:"require_all_software_macos"`
RequireAllSoftwareWindows *bool `json:"require_all_software_windows"`
LockEndUserInfo *bool `json:"lock_end_user_info"`
}

View file

@ -238,22 +238,31 @@ func (svc *Service) IsAllSetupExperienceSoftwareRequired(ctx context.Context, ho
}
func isAllSetupExperienceSoftwareRequired(ctx context.Context, ds fleet.Datastore, host *fleet.Host) (bool, error) {
// Only macOS and Windows support canceling setup if software fails.
if host.Platform != "darwin" && host.Platform != "windows" {
return false, nil
}
teamID := host.TeamID
requireAllSoftware := false
if teamID == nil || *teamID == 0 {
ac, err := ds.AppConfig(ctx)
if err != nil {
return false, ctxerr.Wrap(ctx, err, "getting app config")
}
requireAllSoftware = ac.MDM.MacOSSetup.RequireAllSoftware
} else {
team, err := ds.TeamLite(ctx, *teamID)
if err != nil {
return false, ctxerr.Wrap(ctx, err, "load team")
if host.Platform == "windows" {
return ac.MDM.MacOSSetup.RequireAllSoftwareWindows, nil
}
requireAllSoftware = team.Config.MDM.MacOSSetup.RequireAllSoftware
return ac.MDM.MacOSSetup.RequireAllSoftware, nil
}
return requireAllSoftware, nil
team, err := ds.TeamLite(ctx, *teamID)
if err != nil {
return false, ctxerr.Wrap(ctx, err, "load team")
}
if host.Platform == "windows" {
return team.Config.MDM.MacOSSetup.RequireAllSoftwareWindows, nil
}
return team.Config.MDM.MacOSSetup.RequireAllSoftware, nil
}
func (svc *Service) MaybeCancelPendingSetupExperienceSteps(ctx context.Context, host *fleet.Host) error {
@ -261,8 +270,8 @@ func (svc *Service) MaybeCancelPendingSetupExperienceSteps(ctx context.Context,
}
func maybeCancelPendingSetupExperienceSteps(ctx context.Context, ds fleet.Datastore, host *fleet.Host) error {
// If the host is not MacOS, we do nothing.
if host.Platform != "darwin" {
// Only macOS and Windows support canceling setup experience steps.
if host.Platform != "darwin" && host.Platform != "windows" {
return nil
}

View file

@ -221,6 +221,79 @@ func TestSetupExperienceAuth(t *testing.T) {
}
}
func TestIsAllSetupExperienceSoftwareRequired(t *testing.T) {
ds := new(mock.Store)
teamID := uint(1)
// Use different values for macOS vs Windows to ensure the correct field is read for each platform.
appCfg := &fleet.AppConfig{}
appCfg.MDM.MacOSSetup.RequireAllSoftware = true
appCfg.MDM.MacOSSetup.RequireAllSoftwareWindows = false
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return appCfg, nil
}
ds.TeamLiteFunc = func(ctx context.Context, tid uint) (*fleet.TeamLite, error) {
return &fleet.TeamLite{
ID: tid,
Name: "team",
Config: fleet.TeamConfigLite{
MDM: fleet.TeamMDM{
MacOSSetup: fleet.MacOSSetup{
RequireAllSoftware: false,
RequireAllSoftwareWindows: true,
},
},
},
}, nil
}
tests := []struct {
name string
host *fleet.Host
expected bool
}{
{
name: "macOS host, no team, reads macOS global config (true)",
host: &fleet.Host{Platform: "darwin"},
expected: true,
},
{
name: "macOS host, with team, reads macOS team config (false)",
host: &fleet.Host{Platform: "darwin", TeamID: &teamID},
expected: false,
},
{
name: "windows host, no team, reads Windows global config (false)",
host: &fleet.Host{Platform: "windows"},
expected: false,
},
{
name: "windows host, with team, reads Windows team config (true)",
host: &fleet.Host{Platform: "windows", TeamID: &teamID},
expected: true,
},
{
name: "linux host returns false",
host: &fleet.Host{Platform: "ubuntu"},
expected: false,
},
{
name: "ios host returns false",
host: &fleet.Host{Platform: "ios"},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := isAllSetupExperienceSoftwareRequired(t.Context(), ds, tt.host)
require.NoError(t, err)
require.Equal(t, tt.expected, result)
})
}
}
func TestMaybeUpdateSetupExperience(t *testing.T) {
ds := new(mock.Store)
// _, ctx := newTestService(t, ds, nil, nil, nil)

View file

@ -156,6 +156,7 @@ github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware AppStoreID string
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware PackagePath string
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup ManualAgentInstall optjson.Bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup RequireAllSoftware bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup RequireAllSoftwareWindows bool
github.com/fleetdm/fleet/v4/server/fleet/MDM MacOSMigration fleet.MacOSMigration
github.com/fleetdm/fleet/v4/server/fleet/MacOSMigration Enable bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSMigration Mode fleet.MacOSMigrationMode string

View file

@ -72,6 +72,7 @@ github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware AppStoreID string
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware PackagePath string
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup ManualAgentInstall optjson.Bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup RequireAllSoftware bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup RequireAllSoftwareWindows bool
github.com/fleetdm/fleet/v4/server/fleet/TeamMDM WindowsSettings fleet.WindowsSettings
github.com/fleetdm/fleet/v4/server/fleet/WindowsSettings CustomSettings optjson.Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec]
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec] Set bool

View file

@ -43,6 +43,7 @@ github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware AppStoreID string
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetupSoftware PackagePath string
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup ManualAgentInstall optjson.Bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup RequireAllSoftware bool
github.com/fleetdm/fleet/v4/server/fleet/MacOSSetup RequireAllSoftwareWindows bool
github.com/fleetdm/fleet/v4/server/fleet/TeamMDM WindowsSettings fleet.WindowsSettings
github.com/fleetdm/fleet/v4/server/fleet/WindowsSettings CustomSettings optjson.Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec]
github.com/fleetdm/fleet/v4/pkg/optjson/Slice[github.com/fleetdm/fleet/v4/server/fleet.MDMProfileSpec] Set bool