Update categorization of Windows OS updates to exclude from user-defined Windows MDM profiles in API responses (#15924)

This commit is contained in:
Sarah Gillespie 2024-01-05 09:28:54 -06:00 committed by GitHub
parent ca948da440
commit fa14eaf63a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 264 additions and 34 deletions

View file

@ -0,0 +1,3 @@
- Modified hosts and labels endpoints so that only user-defined Windows MDM profiles are included in
filtered results, host details, and profiles summaries API responses (more specifically,
"Windows OS updates" is excluded from these responses).

View file

@ -1077,7 +1077,10 @@ func (ds *Datastore) applyHostFilters(
}
return "", nil, err
} else if opt.OSSettingsFilter.IsValid() {
sqlStmt, params = ds.filterHostsByOSSettingsStatus(sqlStmt, opt, params, enableDiskEncryption)
sqlStmt, params, err = ds.filterHostsByOSSettingsStatus(sqlStmt, opt, params, enableDiskEncryption)
if err != nil {
return "", nil, err
}
} else if opt.OSSettingsDiskEncryptionFilter.IsValid() {
sqlStmt, params = ds.filterHostsByOSSettingsDiskEncryptionStatus(sqlStmt, opt, params, enableDiskEncryption)
}
@ -1234,9 +1237,9 @@ func filterHostsByMacOSDiskEncryptionStatus(sql string, opt fleet.HostListOption
return sql + fmt.Sprintf(` AND EXISTS (%s)`, subquery), append(params, subqueryParams...)
}
func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostListOptions, params []interface{}, isDiskEncryptionEnabled bool) (string, []interface{}) {
func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostListOptions, params []interface{}, isDiskEncryptionEnabled bool) (string, []interface{}, error) {
if !opt.OSSettingsFilter.IsValid() {
return sql, params
return sql, params, nil
}
// TODO: Look into ways we can convert some of the LEFT JOINs in the main list hosts query
@ -1278,13 +1281,25 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
// construct the WHERE for windows
whereWindows = `hmdm.name = ? AND hmdm.enrolled = 1 AND hmdm.is_server = 0`
paramsWindows := []interface{}{fleet.WellKnownMDMFleet}
subqueryFailed, paramsFailed := subqueryHostsMDMWindowsOSSettingsStatusFailed()
subqueryFailed, paramsFailed, err := subqueryHostsMDMWindowsOSSettingsStatusFailed()
if err != nil {
return "", nil, err
}
paramsWindows = append(paramsWindows, paramsFailed...)
subqueryPending, paramsPending := subqueryHostsMDMWindowsOSSettingsStatusPending()
subqueryPending, paramsPending, err := subqueryHostsMDMWindowsOSSettingsStatusPending()
if err != nil {
return "", nil, err
}
paramsWindows = append(paramsWindows, paramsPending...)
subqueryVerifying, paramsVerifying := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
subqueryVerifying, paramsVerifying, err := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
if err != nil {
return "", nil, err
}
paramsWindows = append(paramsWindows, paramsVerifying...)
subqueryVerified, paramsVerified := subqueryHostsMDMWindowsOSSettingsStatusVerified()
subqueryVerified, paramsVerified, err := subqueryHostsMDMWindowsOSSettingsStatusVerified()
if err != nil {
return "", nil, err
}
paramsWindows = append(paramsWindows, paramsVerified...)
profilesStatus := fmt.Sprintf(`
@ -1365,7 +1380,7 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
params = append(params, paramsWindows...)
params = append(params, paramsMacOS...)
return sql + fmt.Sprintf(sqlFmt, whereWindows, whereMacOS), params
return sql + fmt.Sprintf(sqlFmt, whereWindows, whereMacOS), params, nil
}
func (ds *Datastore) filterHostsByOSSettingsDiskEncryptionStatus(sql string, opt fleet.HostListOptions, params []interface{}, enableDiskEncryption bool) (string, []interface{}) {

View file

@ -593,7 +593,10 @@ func (ds *Datastore) applyHostLabelFilters(ctx context.Context, filter fleet.Tea
if enableDiskEncryption, err := ds.getConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil {
return "", nil, err
} else if opt.OSSettingsFilter.IsValid() {
query, params = ds.filterHostsByOSSettingsStatus(query, opt, params, enableDiskEncryption)
query, params, err = ds.filterHostsByOSSettingsStatus(query, opt, params, enableDiskEncryption)
if err != nil {
return "", nil, err
}
} else if opt.OSSettingsDiskEncryptionFilter.IsValid() {
query, params = ds.filterHostsByOSSettingsDiskEncryptionStatus(query, opt, params, enableDiskEncryption)
}

View file

@ -729,40 +729,46 @@ func (ds *Datastore) DeleteMDMWindowsConfigProfileByTeamAndName(ctx context.Cont
return nil
}
func subqueryHostsMDMWindowsOSSettingsStatusFailed() (string, []interface{}) {
func subqueryHostsMDMWindowsOSSettingsStatusFailed() (string, []interface{}, error) {
sql := `
SELECT
1 FROM host_mdm_windows_profiles hmwp
WHERE
h.uuid = hmwp.host_uuid
AND hmwp.status = ?`
AND hmwp.status = ?
AND hmwp.profile_name NOT IN(?)`
args := []interface{}{
fleet.MDMDeliveryFailed,
mdm.ListFleetReservedWindowsProfileNames(),
}
return sql, args
return sqlx.In(sql, args...)
}
func subqueryHostsMDMWindowsOSSettingsStatusPending() (string, []interface{}) {
func subqueryHostsMDMWindowsOSSettingsStatusPending() (string, []interface{}, error) {
sql := `
SELECT
1 FROM host_mdm_windows_profiles hmwp
WHERE
h.uuid = hmwp.host_uuid
AND (hmwp.status IS NULL OR hmwp.status = ?)
AND hmwp.profile_name NOT IN(?)
AND NOT EXISTS (
SELECT
1 FROM host_mdm_windows_profiles hmwp2
WHERE (h.uuid = hmwp2.host_uuid
AND hmwp2.status = ?))`
AND hmwp2.status = ?
AND hmwp2.profile_name NOT IN(?)))`
args := []interface{}{
fleet.MDMDeliveryPending,
mdm.ListFleetReservedWindowsProfileNames(),
fleet.MDMDeliveryFailed,
mdm.ListFleetReservedWindowsProfileNames(),
}
return sql, args
return sqlx.In(sql, args...)
}
func subqueryHostsMDMWindowsOSSettingsStatusVerifying() (string, []interface{}) {
func subqueryHostsMDMWindowsOSSettingsStatusVerifying() (string, []interface{}, error) {
sql := `
SELECT
1 FROM host_mdm_windows_profiles hmwp
@ -770,25 +776,28 @@ func subqueryHostsMDMWindowsOSSettingsStatusVerifying() (string, []interface{})
h.uuid = hmwp.host_uuid
AND hmwp.operation_type = ?
AND hmwp.status = ?
AND hmwp.profile_name NOT IN(?)
AND NOT EXISTS (
SELECT
1 FROM host_mdm_windows_profiles hmwp2
WHERE (h.uuid = hmwp2.host_uuid
AND hmwp2.operation_type = ?
AND hmwp2.profile_name NOT IN(?)
AND(hmwp2.status IS NULL
OR hmwp2.status NOT IN(?, ?))))`
OR hmwp2.status NOT IN(?))))`
args := []interface{}{
fleet.MDMOperationTypeInstall,
fleet.MDMDeliveryVerifying,
mdm.ListFleetReservedWindowsProfileNames(),
fleet.MDMOperationTypeInstall,
fleet.MDMDeliveryVerifying,
fleet.MDMDeliveryVerified,
mdm.ListFleetReservedWindowsProfileNames(),
[]interface{}{fleet.MDMDeliveryVerifying, fleet.MDMDeliveryVerified},
}
return sql, args
return sqlx.In(sql, args...)
}
func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}) {
func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}, error) {
sql := `
SELECT
1 FROM host_mdm_windows_profiles hmwp
@ -796,20 +805,24 @@ func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}) {
h.uuid = hmwp.host_uuid
AND hmwp.operation_type = ?
AND hmwp.status = ?
AND hmwp.profile_name NOT IN(?)
AND NOT EXISTS (
SELECT
1 FROM host_mdm_windows_profiles hmwp2
WHERE (h.uuid = hmwp2.host_uuid
AND hmwp2.operation_type = ?
AND hmwp2.profile_name NOT IN(?)
AND(hmwp2.status IS NULL
OR hmwp2.status != ?)))`
args := []interface{}{
fleet.MDMOperationTypeInstall,
fleet.MDMDeliveryVerified,
mdm.ListFleetReservedWindowsProfileNames(),
fleet.MDMOperationTypeInstall,
mdm.ListFleetReservedWindowsProfileNames(),
fleet.MDMDeliveryVerified,
}
return sql, args
return sqlx.In(sql, args...)
}
func (ds *Datastore) GetMDMWindowsProfilesSummary(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
@ -856,13 +869,25 @@ type statusCounts struct {
func getMDMWindowsStatusCountsProfilesOnlyDB(ctx context.Context, ds *Datastore, teamID *uint) ([]statusCounts, error) {
var args []interface{}
subqueryFailed, subqueryFailedArgs := subqueryHostsMDMWindowsOSSettingsStatusFailed()
subqueryFailed, subqueryFailedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusFailed()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusFailed")
}
args = append(args, subqueryFailedArgs...)
subqueryPending, subqueryPendingArgs := subqueryHostsMDMWindowsOSSettingsStatusPending()
subqueryPending, subqueryPendingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusPending()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusPending")
}
args = append(args, subqueryPendingArgs...)
subqueryVerifying, subqueryVeryingingArgs := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
subqueryVerifying, subqueryVeryingingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerifying")
}
args = append(args, subqueryVeryingingArgs...)
subqueryVerified, subqueryVerifiedArgs := subqueryHostsMDMWindowsOSSettingsStatusVerified()
subqueryVerified, subqueryVerifiedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerified()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerified")
}
args = append(args, subqueryVerifiedArgs...)
teamFilter := "h.team_id IS NULL"
@ -907,7 +932,7 @@ GROUP BY
)
var counts []statusCounts
err := sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
err = sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
if err != nil {
return nil, err
}
@ -916,13 +941,25 @@ GROUP BY
func getMDMWindowsStatusCountsProfilesAndBitLockerDB(ctx context.Context, ds *Datastore, teamID *uint) ([]statusCounts, error) {
var args []interface{}
subqueryFailed, subqueryFailedArgs := subqueryHostsMDMWindowsOSSettingsStatusFailed()
subqueryFailed, subqueryFailedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusFailed()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusFailed")
}
args = append(args, subqueryFailedArgs...)
subqueryPending, subqueryPendingArgs := subqueryHostsMDMWindowsOSSettingsStatusPending()
subqueryPending, subqueryPendingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusPending()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusPending")
}
args = append(args, subqueryPendingArgs...)
subqueryVerifying, subqueryVeryingingArgs := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
subqueryVerifying, subqueryVeryingingArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerifying()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerifying")
}
args = append(args, subqueryVeryingingArgs...)
subqueryVerified, subqueryVerifiedArgs := subqueryHostsMDMWindowsOSSettingsStatusVerified()
subqueryVerified, subqueryVerifiedArgs, err := subqueryHostsMDMWindowsOSSettingsStatusVerified()
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "subqueryHostsMDMWindowsOSSettingsStatusVerified")
}
args = append(args, subqueryVerifiedArgs...)
profilesStatus := fmt.Sprintf(`
@ -1030,7 +1067,7 @@ GROUP BY
)
var counts []statusCounts
err := sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
err = sqlx.SelectContext(ctx, ds.reader(ctx), &counts, stmt, args...)
if err != nil {
return nil, err
}
@ -1651,7 +1688,7 @@ SELECT
FROM
host_mdm_windows_profiles
WHERE
host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s', '%s'))`,
host_uuid = ? AND profile_name NOT IN(?) AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s', '%s'))`,
fleet.MDMDeliveryPending,
fleet.MDMOperationTypeRemove,
fleet.MDMDeliveryPending,
@ -1659,8 +1696,13 @@ host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s',
fleet.MDMDeliveryVerified,
)
stmt, args, err := sqlx.In(stmt, hostUUID, mdm.ListFleetReservedWindowsProfileNames())
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "building in statement")
}
var profiles []fleet.HostMDMWindowsProfile
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &profiles, stmt, hostUUID); err != nil {
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &profiles, stmt, args...); err != nil {
return nil, err
}
return profiles, nil

View file

@ -80,3 +80,9 @@ func FleetReservedProfileNames() map[string]struct{} {
FleetWindowsOSUpdatesProfileName: {},
}
}
// ListFleetReservedWindowsProfileNames returns a list of PayloadDisplayName strings
// that are reserved by Fleet for Windows.
func ListFleetReservedWindowsProfileNames() []string {
return []string{FleetWindowsOSUpdatesProfileName}
}

View file

@ -259,6 +259,8 @@ func (s *integrationMDMTestSuite) TearDownTest() {
appCfg.MDM.WindowsEnabledAndConfigured = true
// ensure global disk encryption is disabled on exit
appCfg.MDM.EnableDiskEncryption = optjson.SetBool(false)
// ensure global Windows OS updates are always disabled for the next test
appCfg.MDM.WindowsUpdates = mdm_types.WindowsUpdates{}
err := s.ds.SaveAppConfig(ctx, &appCfg.AppConfig)
require.NoError(t, err)
@ -10049,10 +10051,68 @@ func (s *integrationMDMTestSuite) TestWindowsProfileManagement() {
require.NotNil(t, p.Status)
require.Equal(t, wantStatus, *p.Status, "profile", p.Name)
require.Equal(t, "windows", p.Platform)
// Fleet reserved profiles (e.g., OS updates) should be screened from the host details response
require.NotContains(t, servermdm.ListFleetReservedWindowsProfileNames(), p.Name)
}
require.ElementsMatch(t, wantProfs, gotProfs)
}
checkHostsFilteredByOSSettingsStatus := func(t *testing.T, wantHosts []string, wantStatus fleet.MDMDeliveryStatus, teamID *uint, labels ...*fleet.Label) {
var teamFilter string
if teamID != nil {
teamFilter = fmt.Sprintf("&team_id=%d", *teamID)
}
var gotHostsResp listHostsResponse
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts?os_settings=%s%s", wantStatus, teamFilter), nil, http.StatusOK, &gotHostsResp)
require.NotNil(t, gotHostsResp.Hosts)
var gotHosts []string
for _, h := range gotHostsResp.Hosts {
gotHosts = append(gotHosts, h.Hostname)
}
require.ElementsMatch(t, wantHosts, gotHosts)
var countHostsResp countHostsResponse
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts/count?os_settings=%s%s", wantStatus, teamFilter), nil, http.StatusOK, &countHostsResp)
require.Equal(t, len(wantHosts), countHostsResp.Count)
for _, l := range labels {
gotHostsResp = listHostsResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/labels/%d/hosts?os_settings=%s%s", l.ID, wantStatus, teamFilter), nil, http.StatusOK, &gotHostsResp)
require.NotNil(t, gotHostsResp.Hosts)
gotHosts = []string{}
for _, h := range gotHostsResp.Hosts {
gotHosts = append(gotHosts, h.Hostname)
}
require.ElementsMatch(t, wantHosts, gotHosts, "label", l.Name)
countHostsResp = countHostsResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/hosts/count?label_id=%d&os_settings=%s%s", l.ID, wantStatus, teamFilter), nil, http.StatusOK, &countHostsResp)
}
}
getProfileUUID := func(t *testing.T, profName string, teamID *uint) string {
var profUUID string
mysql.ExecAdhocSQL(t, s.ds, func(tx sqlx.ExtContext) error {
var globalOrTeamID uint
if teamID != nil {
globalOrTeamID = *teamID
}
return sqlx.GetContext(ctx, tx, &profUUID, `SELECT profile_uuid FROM mdm_windows_configuration_profiles WHERE team_id = ? AND name = ?`, globalOrTeamID, profName)
})
require.NotNil(t, profUUID)
return profUUID
}
checkHostProfileStatus := func(t *testing.T, hostUUID string, profUUID string, wantStatus fleet.MDMDeliveryStatus) {
var gotStatus fleet.MDMDeliveryStatus
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
stmt := `SELECT status FROM host_mdm_windows_profiles WHERE host_uuid = ? AND profile_uuid = ?`
err := sqlx.GetContext(context.Background(), q, &gotStatus, stmt, hostUUID, profUUID)
return err
})
require.Equal(t, wantStatus, gotStatus)
}
// Create a host and then enroll to MDM.
host, mdmDevice := createWindowsHostThenEnrollMDM(s.ds, s.server.URL, t)
// trigger a profile sync
@ -10060,9 +10120,68 @@ func (s *integrationMDMTestSuite) TestWindowsProfileManagement() {
checkHostsProfilesMatch(host, globalProfiles)
checkHostDetails(t, host, globalProfiles, fleet.MDMDeliveryVerifying)
// create new label that includes host
label := &fleet.Label{
Name: t.Name() + "foo",
Query: "select * from foo;",
}
label, err = s.ds.NewLabel(context.Background(), label)
require.NoError(t, err)
require.NoError(t, s.ds.RecordLabelQueryExecutions(ctx, host, map[uint]*bool{label.ID: ptr.Bool(true)}, time.Now(), false))
// simulate osquery reporting host mdm details (host_mdm.enrolled = 1 is condition for
// hosts filtering by os settings status and generating mdm profiles summaries)
require.NoError(t, s.ds.SetOrUpdateMDMData(ctx, host.ID, false, true, s.server.URL, false, fleet.WellKnownMDMFleet, ""))
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, nil, label)
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
Verifying: 1,
}, nil)
// another sync shouldn't return profiles
verifyProfiles(mdmDevice, 0, false)
// make fleet add a Windows OS Updates profile
acResp := appConfigResponse{}
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{"mdm": { "windows_updates": {"deadline_days": 1, "grace_period_days": 1} }}`), http.StatusOK, &acResp)
osUpdatesProf := getProfileUUID(t, servermdm.FleetWindowsOSUpdatesProfileName, nil)
// os updates is sent via a profiles commands
verifyProfiles(mdmDevice, 1, false)
checkHostsProfilesMatch(host, append(globalProfiles, osUpdatesProf))
// but is hidden from host details response
checkHostDetails(t, host, globalProfiles, fleet.MDMDeliveryVerifying)
// os updates profile status doesn't matter for filtered hosts results or summaries
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryVerifying)
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, nil, label)
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
Verifying: 1,
}, nil)
// force os updates profile to failed, doesn't impact filtered hosts results or summaries
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
_, err := q.ExecContext(context.Background(), stmt, osUpdatesProf)
return err
})
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryFailed)
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, nil, label)
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
Verifying: 1,
}, nil)
// force another profile to failed, does impact filtered hosts results and summaries
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
_, err := q.ExecContext(context.Background(), stmt, globalProfiles[0])
return err
})
checkHostProfileStatus(t, host.UUID, globalProfiles[0], fleet.MDMDeliveryFailed)
checkHostsFilteredByOSSettingsStatus(t, []string{}, fleet.MDMDeliveryVerifying, nil, label) // expect no hosts
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryFailed, nil, label) // expect host
s.checkMDMProfilesSummaries(t, nil, fleet.MDMProfilesSummary{
Failed: 1,
Verifying: 0,
}, nil)
// add the host to a team
err = s.ds.AddHostsToTeam(ctx, &tm.ID, []uint{host.ID})
require.NoError(t, err)
@ -10115,6 +10234,48 @@ func (s *integrationMDMTestSuite) TestWindowsProfileManagement() {
// another sync shouldn't return profiles
verifyProfiles(mdmDevice, 0, false)
// make fleet add a Windows OS Updates profile
tmResp := teamResponse{}
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID), json.RawMessage(`{"mdm": { "windows_updates": {"deadline_days": 1, "grace_period_days": 1} }}`), http.StatusOK, &tmResp)
osUpdatesProf = getProfileUUID(t, servermdm.FleetWindowsOSUpdatesProfileName, &tm.ID)
// os updates is sent via a profiles commands
verifyProfiles(mdmDevice, 1, false)
checkHostsProfilesMatch(host, append(teamProfiles, osUpdatesProf))
// but is hidden from host details response
checkHostDetails(t, host, teamProfiles, fleet.MDMDeliveryVerifying)
// os updates profile status doesn't matter for filtered hosts results or summaries
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryVerifying)
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, &tm.ID, label)
s.checkMDMProfilesSummaries(t, &tm.ID, fleet.MDMProfilesSummary{
Verifying: 1,
}, nil)
// force os updates profile to failed, doesn't impact filtered hosts results or summaries
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
_, err := q.ExecContext(context.Background(), stmt, osUpdatesProf)
return err
})
checkHostProfileStatus(t, host.UUID, osUpdatesProf, fleet.MDMDeliveryFailed)
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryVerifying, &tm.ID, label)
s.checkMDMProfilesSummaries(t, &tm.ID, fleet.MDMProfilesSummary{
Verifying: 1,
}, nil)
// force another profile to failed, does impact filtered hosts results and summaries
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
stmt := `UPDATE host_mdm_windows_profiles SET status = 'failed' WHERE profile_uuid = ?`
_, err := q.ExecContext(context.Background(), stmt, teamProfiles[0])
return err
})
checkHostProfileStatus(t, host.UUID, teamProfiles[0], fleet.MDMDeliveryFailed)
checkHostsFilteredByOSSettingsStatus(t, []string{}, fleet.MDMDeliveryVerifying, &tm.ID, label) // expect no hosts
checkHostsFilteredByOSSettingsStatus(t, []string{host.Hostname}, fleet.MDMDeliveryFailed, &tm.ID, label) // expect host
s.checkMDMProfilesSummaries(t, &tm.ID, fleet.MDMProfilesSummary{
Failed: 1,
Verifying: 0,
}, nil)
}
func (s *integrationMDMTestSuite) TestAppConfigMDMWindowsProfiles() {