diff --git a/docs/Using-Fleet/Audit-Activities.md b/docs/Using-Fleet/Audit-Activities.md
index 395f7dc61a..e5c19d9d15 100644
--- a/docs/Using-Fleet/Audit-Activities.md
+++ b/docs/Using-Fleet/Audit-Activities.md
@@ -553,6 +553,7 @@ This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.
- "installed_from_dep": Whether the host was enrolled via DEP.
+- "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility.
#### Example
@@ -560,7 +561,8 @@ This activity contains the following fields:
{
"host_serial": "C08VQ2AXHT96",
"host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)",
- "installed_from_dep": true
+ "installed_from_dep": true,
+ "mdm_platform": "apple"
}
```
diff --git a/frontend/interfaces/activity.ts b/frontend/interfaces/activity.ts
index fcb6dcfd92..92e6afdbdd 100644
--- a/frontend/interfaces/activity.ts
+++ b/frontend/interfaces/activity.ts
@@ -82,6 +82,7 @@ export interface IActivityDetails {
host_display_names?: string[];
host_ids?: number[];
installed_from_dep?: boolean;
+ mdm_platform?: "microsoft" | "apple";
minimum_version?: string;
deadline?: string;
profile_name?: string;
diff --git a/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tests.tsx b/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tests.tsx
index ec6389c781..4e6726c022 100644
--- a/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tests.tsx
+++ b/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tests.tsx
@@ -708,4 +708,65 @@ describe("Activity Feed", () => {
expect(screen.queryByText("baz")).toBeNull();
expect(screen.getByText("Alphas", { exact: false })).toBeInTheDocument();
});
+
+ it("renders a 'mdm_enrolled' type for apple if mdm_platform is not provided", () => {
+ const activity = createMockActivity({
+ type: ActivityType.MdmEnrolled,
+ details: {
+ host_serial: "ABCD",
+ },
+ });
+ render();
+
+ expect(
+ screen.getByText((content, node) => {
+ return (
+ node?.innerHTML ===
+ "Test User An end user turned on MDM features for a host with serial number ABCD (manual)."
+ );
+ })
+ ).toBeInTheDocument();
+ });
+
+ it("renders a 'mdm_enrolled' type for apple with all details provided", () => {
+ const activity = createMockActivity({
+ type: ActivityType.MdmEnrolled,
+ details: {
+ host_serial: "ABCD",
+ installed_from_dep: true,
+ mdm_platform: "apple",
+ },
+ });
+ render();
+
+ expect(
+ screen.getByText((content, node) => {
+ return (
+ node?.innerHTML ===
+ "Test User An end user turned on MDM features for a host with serial number ABCD (automatic)."
+ );
+ })
+ ).toBeInTheDocument();
+ });
+
+ it("renders a 'mdm_enrolled' type activity for windows hosts.", () => {
+ const activity = createMockActivity({
+ type: ActivityType.MdmEnrolled,
+ details: {
+ mdm_platform: "microsoft",
+ host_display_name: "ABCD",
+ },
+ });
+ render();
+
+ expect(
+ screen.getByText((content, node) => {
+ console.log(node?.innerHTML);
+ return (
+ node?.innerHTML ===
+ "Test User Mobile device management (MDM) was turned on for ABCD (manual)."
+ );
+ })
+ ).toBeInTheDocument();
+ });
});
diff --git a/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx b/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx
index b508ca5030..2d0646d290 100644
--- a/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx
+++ b/frontend/pages/DashboardPage/cards/ActivityFeed/ActivityItem/ActivityItem.tsx
@@ -215,6 +215,17 @@ const TAGGED_TEMPLATES = {
);
},
mdmEnrolled: (activity: IActivity) => {
+ if (activity.details?.mdm_platform === "microsoft") {
+ return (
+ <>
+ Mobile device management (MDM) was turned on for{" "}
+ {activity.details?.host_display_name} (manual).
+ >
+ );
+ }
+
+ // note: if mdm_platform is missing, we assume this is Apple MDM for backwards
+ // compatibility
return (
<>
An end user turned on MDM features for a host with serial number{" "}
diff --git a/server/fleet/activities.go b/server/fleet/activities.go
index 4c6adfbcd4..86436a062d 100644
--- a/server/fleet/activities.go
+++ b/server/fleet/activities.go
@@ -692,6 +692,7 @@ type ActivityTypeMDMEnrolled struct {
HostSerial string `json:"host_serial"`
HostDisplayName string `json:"host_display_name"`
InstalledFromDEP bool `json:"installed_from_dep"`
+ MDMPlatform string `json:"mdm_platform"`
}
func (a ActivityTypeMDMEnrolled) ActivityName() string {
@@ -703,10 +704,12 @@ func (a ActivityTypeMDMEnrolled) Documentation() (activity string, details strin
`This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.
-- "installed_from_dep": Whether the host was enrolled via DEP.`, `{
+- "installed_from_dep": Whether the host was enrolled via DEP.
+- "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility.`, `{
"host_serial": "C08VQ2AXHT96",
"host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)",
- "installed_from_dep": true
+ "installed_from_dep": true,
+ "mdm_platform": "apple"
}`
}
diff --git a/server/fleet/mdm.go b/server/fleet/mdm.go
index 928249ec73..cf5c2b0a29 100644
--- a/server/fleet/mdm.go
+++ b/server/fleet/mdm.go
@@ -7,6 +7,11 @@ import (
"time"
)
+const (
+ MDMPlatformApple = "apple"
+ MDMPlatformMicrosoft = "microsoft"
+)
+
type AppleMDM struct {
CommonName string `json:"common_name"`
SerialNumber string `json:"serial_number"`
diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go
index d3c5252dfb..ac3b584e4a 100644
--- a/server/service/apple_mdm.go
+++ b/server/service/apple_mdm.go
@@ -2211,6 +2211,7 @@ func (svc *MDMAppleCheckinAndCommandService) Authenticate(r *mdm.Request, m *mdm
HostSerial: info.HardwareSerial,
HostDisplayName: info.DisplayName,
InstalledFromDEP: info.InstalledFromDEP,
+ MDMPlatform: fleet.MDMPlatformApple,
})
}
diff --git a/server/service/apple_mdm_test.go b/server/service/apple_mdm_test.go
index a2a7eb3e03..42a3377dc6 100644
--- a/server/service/apple_mdm_test.go
+++ b/server/service/apple_mdm_test.go
@@ -972,6 +972,7 @@ func TestMDMAuthenticate(t *testing.T) {
require.Equal(t, serial, a.HostSerial)
require.Equal(t, a.HostDisplayName, fmt.Sprintf("%s (%s)", model, serial))
require.False(t, a.InstalledFromDEP)
+ require.Equal(t, fleet.MDMPlatformApple, a.MDMPlatform)
return nil
}
diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go
index 3651293025..ad13153d90 100644
--- a/server/service/integration_mdm_test.go
+++ b/server/service/integration_mdm_test.go
@@ -925,7 +925,7 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() {
require.JSONEq(
t,
fmt.Sprintf(
- `{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true}`,
+ `{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`,
devices[0].SerialNumber, devices[0].Model, devices[0].SerialNumber,
),
string(*activity.Details),
@@ -1068,8 +1068,8 @@ func (s *integrationMDMTestSuite) TestAppleMDMDeviceEnrollment() {
}
}
require.Len(t, details, 2)
- require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), string(*details[len(details)-2]))
- require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false}`, mdmDeviceB.SerialNumber, mdmDeviceB.Model, mdmDeviceB.SerialNumber), string(*details[len(details)-1]))
+ require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), string(*details[len(details)-2]))
+ require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceB.SerialNumber, mdmDeviceB.Model, mdmDeviceB.SerialNumber), string(*details[len(details)-1]))
// set an enroll secret
var applyResp applyEnrollSecretSpecResponse
@@ -5414,6 +5414,17 @@ func (s *integrationMDMTestSuite) TestValidRequestSecurityTokenRequest() {
require.True(t, s.isXMLTagContentPresent("TokenType", resSoapMsg))
require.True(t, s.isXMLTagContentPresent("RequestID", resSoapMsg))
require.True(t, s.isXMLTagContentPresent("BinarySecurityToken", resSoapMsg))
+
+ // Checking if an activity was created for the enrollment
+ s.lastActivityOfTypeMatches(
+ fleet.ActivityTypeMDMEnrolled{}.ActivityName(),
+ `{
+ "mdm_platform": "microsoft",
+ "host_serial": "",
+ "installed_from_dep": false,
+ "host_display_name": "DESKTOP-0C89RC0"
+ }`,
+ 0)
}
func (s *integrationMDMTestSuite) TestInvalidRequestSecurityTokenRequestWithMissingAdditionalContext() {
diff --git a/server/service/microsoft_mdm.go b/server/service/microsoft_mdm.go
index 5010484939..c784b8a65c 100644
--- a/server/service/microsoft_mdm.go
+++ b/server/service/microsoft_mdm.go
@@ -819,10 +819,15 @@ func (svc *Service) GetMDMWindowsEnrollResponse(ctx context.Context, secTokenMsg
return nil, ctxerr.Wrap(ctx, err, "creation of RequestSecurityTokenResponseCollection message")
}
- // RequestSecurityTokenResponseCollection message is ready
- // The identity and provisioning information will be sent to the Windows MDM Enrollment Client
+ // RequestSecurityTokenResponseCollection message is ready. The identity
+ // and provisioning information will be sent to the Windows MDM
+ // Enrollment Client
- // But before doing that, let's save the device information to the list of MDM enrolled MDM devices
+ // But before doing that, let's save the device information to the list
+ // of MDM enrolled MDM devices
+ //
+ // This method also creates the relevant enrollment activity as it has
+ // access to the device information.
err = svc.storeWindowsMDMEnrolledDevice(ctx, secTokenMsg)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "enrolled device information cannot be stored")
@@ -1004,6 +1009,19 @@ func (svc *Service) storeWindowsMDMEnrolledDevice(ctx context.Context, secTokenM
return err
}
+ err = svc.ds.NewActivity(ctx, nil, &fleet.ActivityTypeMDMEnrolled{
+ HostDisplayName: reqDeviceName,
+ MDMPlatform: fleet.MDMPlatformMicrosoft,
+ })
+ if err != nil {
+ // only logging, the device is enrolled at this point, and we
+ // wouldn't want to fail the request because there was a problem
+ // creating an activity feed item.
+ logging.WithExtras(logging.WithNoUser(ctx),
+ "msg", "failed to generate windows MDM enrolled activity",
+ )
+ }
+
return nil
}