diff --git a/changes/issue-11952-UI-for-windows-mdm-on-off b/changes/issue-11952-UI-for-windows-mdm-on-off
new file mode 100644
index 0000000000..4a6eaa7f6e
--- /dev/null
+++ b/changes/issue-11952-UI-for-windows-mdm-on-off
@@ -0,0 +1 @@
+- add ability to turn windows mdm on and off from the fleet UI
diff --git a/frontend/__mocks__/configMock.ts b/frontend/__mocks__/configMock.ts
index 2ebbd145c9..5feb718f0c 100644
--- a/frontend/__mocks__/configMock.ts
+++ b/frontend/__mocks__/configMock.ts
@@ -124,6 +124,7 @@ const DEFAULT_CONFIG_MOCK: IConfig = {
},
fleet_desktop: { transparency_url: "https://fleetdm.com/transparency" },
mdm: {
+ windows_enabled_and_configured: true,
apple_bm_default_team: "Apples",
apple_bm_enabled_and_configured: true,
apple_bm_terms_expired: false,
diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts
index a4ed1fe9bd..4272989c98 100644
--- a/frontend/interfaces/config.ts
+++ b/frontend/interfaces/config.ts
@@ -47,6 +47,7 @@ export default PropTypes.shape({
enabled_and_configured: PropTypes.bool,
apple_bm_terms_expired: PropTypes.bool,
apple_bm_enabled_and_configured: PropTypes.bool,
+ windows_enabled_and_configured: PropTypes.bool,
macos_updates: PropTypes.shape({
minimum_version: PropTypes.string,
deadline: PropTypes.string,
@@ -116,6 +117,7 @@ export interface IMdmConfig {
apple_bm_default_team?: string;
apple_bm_terms_expired: boolean;
apple_bm_enabled_and_configured: boolean;
+ windows_enabled_and_configured: boolean;
end_user_authentication: IEndUserAuthentication;
macos_updates: {
minimum_version: string;
@@ -282,6 +284,7 @@ export interface IConfig {
};
};
mdm: IMdmConfig;
+ mdm_enabled?: boolean; // TODO: remove when windows MDM is released. Only used for windows MDM dev currently.
}
export interface IWebhookSettings {
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx
index 24cdc05d6f..faba86e35b 100644
--- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx
@@ -8,14 +8,16 @@ import { AppContext } from "context/app";
import mdmAppleAPI from "services/entities/mdm_apple";
import { IMdmApple } from "interfaces/mdm";
+import { readableDate } from "utilities/helpers";
+import PATHS from "router/paths";
+
import Button from "components/buttons/Button";
import CustomLink from "components/CustomLink";
import Spinner from "components/Spinner";
import DataError from "components/DataError";
-import { readableDate } from "utilities/helpers";
-
import RequestCSRModal from "./components/RequestCSRModal";
import EndUserMigrationSection from "./components/EndUserMigrationSection/EndUserMigrationSection";
+import WindowsMdmSection from "./components/WindowsMdmSection/WindowsMdmSection";
const baseClass = "mdm-settings";
@@ -46,6 +48,10 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => {
setShowRequestCSRModal(!showRequestCSRModal);
};
+ const navigateToWindowsMdm = () => {
+ router.push(PATHS.ADMIN_INTEGRATIONS_MDM_WINDOWS);
+ };
+
// The API returns a 404 error if APNs is not configured yet, in that case we
// want to prompt the user to download the certs and keys to configure the
// server instead of the default error message.
@@ -127,10 +133,19 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => {
Apple Push Certificates Portal
{isLoadingMdmApple ? : renderMdmAppleSection()}
+ {/* TODO: remove conditional rendering when windows MDM is released. */}
+ {config?.mdm_enabled && (
+
+ )}
{isPremiumTier && (
-
-
-
+ <>
+
+
+
+ >
)}
{showRequestCSRModal && (
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx
new file mode 100644
index 0000000000..be2e6861b6
--- /dev/null
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx
@@ -0,0 +1,130 @@
+import React, { useContext } from "react";
+import { InjectedRouter } from "react-router";
+
+import PATHS from "router/paths";
+import configAPI from "services/entities/config";
+import { NotificationContext } from "context/notification";
+import { AppContext } from "context/app";
+
+import MainContent from "components/MainContent/MainContent";
+import Button from "components/buttons/Button";
+import BackLink from "components/BackLink/BackLink";
+
+const baseClass = "windows-mdm-page";
+
+interface ISetWindowsMdmOptions {
+ enable: boolean;
+ successMessage: string;
+ errorMessage: string;
+ router: InjectedRouter;
+}
+
+const useSetWindowsMdm = ({
+ enable,
+ successMessage,
+ errorMessage,
+ router,
+}: ISetWindowsMdmOptions) => {
+ const { setConfig } = useContext(AppContext);
+ const { renderFlash } = useContext(NotificationContext);
+
+ const turnOnWindowsMdm = async () => {
+ try {
+ const updatedConfig = await configAPI.update({
+ mdm: {
+ windows_enabled_and_configured: enable,
+ },
+ });
+ setConfig(updatedConfig);
+ renderFlash("success", successMessage);
+ } catch {
+ renderFlash("error", errorMessage);
+ } finally {
+ router.push(PATHS.ADMIN_INTEGRATIONS_MDM);
+ }
+ };
+
+ return turnOnWindowsMdm;
+};
+
+interface IWindowsMdmOnContentProps {
+ router: InjectedRouter;
+}
+
+const WindowsMdmOnContent = ({ router }: IWindowsMdmOnContentProps) => {
+ const turnOnWindowsMdm = useSetWindowsMdm({
+ enable: true,
+ successMessage: "Windows MDM turned on (servers excluded).",
+ errorMessage: "Unable to turn on Windows MDM. Please try again.",
+ router,
+ });
+
+ return (
+ <>
+ Turn on Windows MDM
+
+ This will turn MDM on for Windows hosts with fleetd, overriding existing
+ MDM solutions.
+
+ MDM won't be turned on for Windows servers
+
+ >
+ );
+};
+
+interface IWindowsMdmOffContentProps {
+ router: InjectedRouter;
+}
+
+const WindowsMdmOffContent = ({ router }: IWindowsMdmOffContentProps) => {
+ const turnOffWindowsMdm = useSetWindowsMdm({
+ enable: false,
+ successMessage: "Windows MDM turned off.",
+ errorMessage: "Unable to turn off Windows MDM. Please try again.",
+ router,
+ });
+
+ return (
+ <>
+ Turn off Windows MDM
+ This will turn off MDM on each Windows host.
+
+ >
+ );
+};
+
+interface IWindowsMdmPageProps {
+ router: InjectedRouter;
+}
+
+const WindowsMdmPage = ({ router }: IWindowsMdmPageProps) => {
+ const { config } = useContext(AppContext);
+
+ // TODO: remove when windows MDM is fully released. This is a temporary redirect
+ // when the feature is not enabled.
+ if (!config?.mdm_enabled) {
+ router.replace(PATHS.ADMIN_INTEGRATIONS_MDM);
+ }
+
+ const isWindowsMdmEnabled =
+ config?.mdm?.windows_enabled_and_configured ?? false;
+
+ return (
+
+ <>
+
+ {isWindowsMdmEnabled ? (
+
+ ) : (
+
+ )}
+ >
+
+ );
+};
+
+export default WindowsMdmPage;
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/__styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/__styles.scss
new file mode 100644
index 0000000000..840c887eeb
--- /dev/null
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/__styles.scss
@@ -0,0 +1,15 @@
+.windows-mdm-page {
+
+ &__back-to-mdm {
+ margin-bottom: $pad-xlarge;
+ }
+
+ h1 {
+ margin-bottom: $pad-xxlarge;
+ }
+
+ p {
+ font-size: $x-small;
+ margin: 0 0 $pad-large;
+ }
+}
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/index.ts
new file mode 100644
index 0000000000..b26c35a468
--- /dev/null
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/index.ts
@@ -0,0 +1 @@
+export { default } from "./WindowsMdmPage";
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/WindowsMdmSection.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/WindowsMdmSection.tsx
new file mode 100644
index 0000000000..cf4f2da0ad
--- /dev/null
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/WindowsMdmSection.tsx
@@ -0,0 +1,70 @@
+import React, { useContext } from "react";
+
+import { AppContext } from "context/app";
+
+import Card from "components/Card/Card";
+import Button from "components/buttons/Button";
+import Icon from "components/Icon";
+
+const baseClass = "windows-mdm-section";
+
+interface ITurnOnWindowsMdmProps {
+ onClickTurnOn: () => void;
+}
+const TurnOnWindowsMdm = ({ onClickTurnOn }: ITurnOnWindowsMdmProps) => {
+ return (
+
+
+
Turn on Windows MDM
+
Turn MDM on for Windows hosts with fleetd.
+
+
+
+ );
+};
+
+interface ITurnOffWindowsMdmProps {
+ onClickEdit: () => void;
+}
+
+const TurnOffWindowsMdm = ({ onClickEdit }: ITurnOffWindowsMdmProps) => {
+ return (
+
+
+
+
Windows MDM turned on (servers excluded).
+
+
+
+ );
+};
+
+interface IWindowsMdmSectionProps {
+ turnOnWindowsMdm: () => void;
+ editWindowsMdm: () => void;
+}
+
+const WindowsMdmSection = ({
+ turnOnWindowsMdm,
+ editWindowsMdm,
+}: IWindowsMdmSectionProps) => {
+ const { config } = useContext(AppContext);
+
+ const isWindowsMdmEnabled =
+ config?.mdm?.windows_enabled_and_configured ?? false;
+
+ return (
+
+ {isWindowsMdmEnabled ? (
+
+ ) : (
+
+ )}
+
+ );
+};
+
+export default WindowsMdmSection;
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/_styles.scss
new file mode 100644
index 0000000000..3ed546487a
--- /dev/null
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/_styles.scss
@@ -0,0 +1,33 @@
+.windows-mdm-section {
+ font-size: $x-small;
+
+ p {
+ margin: 0;
+ }
+
+ &__turn-on-windows, &__turn-off-windows {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ &__turn-on-windows {
+ h3 {
+ font-size: $x-small;
+ font-weight: $bold;
+ margin: 0 0 $pad-xsmall;
+ }
+ }
+
+ &__turn-off-windows {
+ > div {
+ display: flex;
+ align-items: center;
+ }
+
+ p {
+ margin-left: $pad-small;
+ }
+ }
+
+}
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/index.ts
new file mode 100644
index 0000000000..2e04c3a5cc
--- /dev/null
+++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/index.ts
@@ -0,0 +1 @@
+export { default } from "./WindowsMdmSection";
diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx
index eed5d3b896..69909599bd 100644
--- a/frontend/router/index.tsx
+++ b/frontend/router/index.tsx
@@ -53,6 +53,7 @@ import AgentOptionsPage from "pages/admin/TeamManagementPage/TeamDetailsWrapper/
import MacOSUpdates from "pages/ManageControlsPage/MacOSUpdates";
import MacOSSettings from "pages/ManageControlsPage/MacOSSettings";
import MacOSSetup from "pages/ManageControlsPage/MacOSSetup/MacOSSetup";
+import WindowsMdmPage from "pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage";
import PATHS from "router/paths";
@@ -136,6 +137,7 @@ const routes = (
+
diff --git a/frontend/router/paths.ts b/frontend/router/paths.ts
index 88e688c7c9..b6230e9f98 100644
--- a/frontend/router/paths.ts
+++ b/frontend/router/paths.ts
@@ -22,6 +22,7 @@ export default {
ADMIN_INTEGRATIONS: `${URL_PREFIX}/settings/integrations`,
ADMIN_INTEGRATIONS_TICKET_DESTINATIONS: `${URL_PREFIX}/settings/integrations/ticket-destinations`,
ADMIN_INTEGRATIONS_MDM: `${URL_PREFIX}/settings/integrations/mdm`,
+ ADMIN_INTEGRATIONS_MDM_WINDOWS: `${URL_PREFIX}/settings/integrations/mdm/windows`,
ADMIN_INTEGRATIONS_AUTOMATIC_ENROLLMENT: `${URL_PREFIX}/settings/integrations/automatic-enrollment`,
ADMIN_TEAMS: `${URL_PREFIX}/settings/teams`,
ADMIN_SETTINGS: `${URL_PREFIX}/settings`,