From 1d1be298a9d308abe51e7bfda97f16f5d3bd0444 Mon Sep 17 00:00:00 2001 From: Steven Palmesano <3100993+spalmesano0@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:18:45 -0500 Subject: [PATCH] Add /enroll URL for macOS in Add hosts modal (#47528) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Related issue:** Resolves #38874 # Checklist for submitter - [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 ## Summary by CodeRabbit * **New Features** * Added macOS enrollment details in the “Add hosts” flow, including a clearer choice between **Personal (BYOD)** and **Company-owned** devices. * Shows a copyable macOS enrollment URL when MDM is configured, updating the URL based on the selected device type. * Keeps the macOS setup experience aligned with the enrollment method, including packaging guidance when MDM isn’t enabled. * **Tests** * Added coverage for macOS enrollment URL rendering and device-type switching in the “Add hosts” modal. --- changes/38874-macos-enroll-url | 1 + .../AddHostsModal/AddHostsModal.tests.tsx | 42 +++++++ .../PlatformWrapper/MacosPanel/MacosPanel.tsx | 109 ++++++++++++++++++ .../PlatformWrapper/MacosPanel/_styles.scss | 6 + .../PlatformWrapper/MacosPanel/index.ts | 1 + .../PlatformWrapper/PlatformWrapper.tsx | 8 +- 6 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 changes/38874-macos-enroll-url create mode 100644 frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/MacosPanel.tsx create mode 100644 frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/_styles.scss create mode 100644 frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/index.ts diff --git a/changes/38874-macos-enroll-url b/changes/38874-macos-enroll-url new file mode 100644 index 0000000000..79d66b0d2c --- /dev/null +++ b/changes/38874-macos-enroll-url @@ -0,0 +1 @@ +- Added enrollment profile URL to the macOS tab in the "Add hosts" modal, with enrollment type selection (company-owned or personal/BYOD) for MDM users. diff --git a/frontend/components/AddHostsModal/AddHostsModal.tests.tsx b/frontend/components/AddHostsModal/AddHostsModal.tests.tsx index 3fa223629b..d596091e61 100644 --- a/frontend/components/AddHostsModal/AddHostsModal.tests.tsx +++ b/frontend/components/AddHostsModal/AddHostsModal.tests.tsx @@ -94,6 +94,48 @@ describe("AddHostsModal", () => { expect(screen.queryByText(/--enable-scripts/i)).not.toBeInTheDocument(); }); + it("renders enroll url input for macOS if mac mdm is enabled", async () => { + const render = createCustomRenderer({ + withBackendMock: true, + context: { + app: { + isMacMdmEnabledAndConfigured: true, + isPreviewMode: false, + config: createMockConfig(), + }, + }, + }); + + const { user } = render( + + ); + + await user.click(screen.getByRole("tab", { name: "macOS" })); + expect(screen.getByLabelText("Personal (BYOD)")).toBeInTheDocument(); + expect(screen.getByLabelText("Company-owned")).toBeInTheDocument(); + expect( + screen.getByText(/Send this to your end users:/i) + ).toBeInTheDocument(); + + // Company-owned is selected by default — URL has no byod param + const urlInput = screen.getByDisplayValue( + new RegExp(`/enroll\\?enroll_secret=${ENROLL_SECRET}$`) + ); + expect(urlInput).toBeInTheDocument(); + + // Switching to Personal (BYOD) appends byod=true + await user.click(screen.getByLabelText("Personal (BYOD)")); + const byodUrlInput = screen.getByDisplayValue( + new RegExp(`/enroll\\?enroll_secret=${ENROLL_SECRET}&byod=true`) + ); + expect(byodUrlInput).toBeInTheDocument(); + }); + it("renders enroll url input for ios & ipadOS if mac mdm is enabled", async () => { const render = createCustomRenderer({ withBackendMock: true, diff --git a/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/MacosPanel.tsx b/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/MacosPanel.tsx new file mode 100644 index 0000000000..fb5982b9f6 --- /dev/null +++ b/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/MacosPanel.tsx @@ -0,0 +1,109 @@ +import React, { useContext, useState } from "react"; + +import { AppContext } from "context/app"; +import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants"; +import { getPathWithQueryParams } from "utilities/url"; +import CustomLink from "components/CustomLink"; +import Radio from "components/forms/fields/Radio"; +import InputField from "components/forms/fields/InputField"; + +type DeviceType = "companyOwned" | "personalBYOD"; + +const generateInstallerString = ( + serverUrl: string, + enrollSecret: string, + scriptsDisabled: boolean +) => { + return `fleetctl package --type=pkg ${ + !scriptsDisabled ? "--enable-scripts " : "" + }--fleet-desktop --fleet-url=${serverUrl} --enroll-secret=${enrollSecret}`; +}; + +const baseClass = "macos-panel"; + +interface IMacosPanelProps { + enrollSecret: string; +} + +const MacosPanel = ({ enrollSecret }: IMacosPanelProps) => { + const { config, isMacMdmEnabledAndConfigured } = useContext(AppContext); + + const [deviceType, setDeviceType] = useState("companyOwned"); + + if (!config) return null; + + if (isMacMdmEnabledAndConfigured) { + const enrollUrl = getPathWithQueryParams( + `${config.server_settings.server_url}/enroll`, + { + enroll_secret: enrollSecret, + byod: deviceType === "personalBYOD" ? "true" : undefined, + } + ); + + return ( +
+
+
+ setDeviceType("personalBYOD")} + /> + setDeviceType("companyOwned")} + /> +
+ + +
+ ); + } + + const installerString = generateInstallerString( + config.server_settings.server_url, + enrollSecret, + config.server_settings.scripts_disabled + ); + + return ( +
+ + Use this command to generate Fleet's agent.{" "} + + + } + type="textarea" + value={installerString} + helpText="Run this on your computer, then deploy the generated package to your hosts." + /> +
+ ); +}; + +export default MacosPanel; diff --git a/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/_styles.scss b/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/_styles.scss new file mode 100644 index 0000000000..e2bb64dcec --- /dev/null +++ b/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/_styles.scss @@ -0,0 +1,6 @@ +.macos-panel { + &__enroll-link input { + font-family: "SourceCodePro", $monospace; + color: $core-fleet-blue; + } +} diff --git a/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/index.ts b/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/index.ts new file mode 100644 index 0000000000..c91b6fb30e --- /dev/null +++ b/frontend/components/AddHostsModal/PlatformWrapper/MacosPanel/index.ts @@ -0,0 +1 @@ +export { default } from "./MacosPanel"; diff --git a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx index 55836caa5b..e450f35262 100644 --- a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx +++ b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx @@ -20,6 +20,7 @@ import TabText from "components/TabText"; import { isValidPemCertificate } from "../../../pages/hosts/ManageHostsPage/helpers"; import IosIpadosPanel from "./IosIpadosPanel"; import AndroidPanel from "./AndroidPanel"; +import MacosPanel from "./MacosPanel"; interface IPlatformSubNav { name: string; @@ -281,9 +282,6 @@ const PlatformWrapper = ({ hosts. For ARM, use --arch=arm64 ); - } else if (packageType === "pkg") { - packageTypeHelpText = - "Run this on your computer, then deploy the generated package to your hosts."; } else { packageTypeHelpText = ""; } @@ -347,6 +345,10 @@ const PlatformWrapper = ({ return ; } + if (packageType === "pkg") { + return ; + } + if (packageType === "advanced") { return ( <>