From bf7ba36fecc532270eafe784fab3fb394b9ca6b4 Mon Sep 17 00:00:00 2001 From: Roel van der Wegen Date: Mon, 13 Apr 2026 23:11:01 +0200 Subject: [PATCH 01/30] Build a license map once --- src/utils/get-cipp-license-translation.js | 34 ++++++++++++++--------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/utils/get-cipp-license-translation.js b/src/utils/get-cipp-license-translation.js index 0397585d927a..321379fc6785 100644 --- a/src/utils/get-cipp-license-translation.js +++ b/src/utils/get-cipp-license-translation.js @@ -3,9 +3,18 @@ import M365LicensesAdditional from "../data/M365Licenses-additional.json"; import { getCachedLicense } from "./cipp-license-cache"; import licenseBackfillManager from "./cipp-license-backfill-manager"; +// Create a Map for O(1) lookups of GUID to Product_Display_Name +const licenseByGuid = new Map(); +[...M365LicensesDefault, ...M365LicensesAdditional].forEach((entry) => { + if (entry.GUID) { + const key = entry.GUID.toLowerCase(); + if (!licenseByGuid.has(key)) { + licenseByGuid.set(key, entry.Product_Display_Name); + } + } +}); + export const getCippLicenseTranslation = (licenseArray) => { - //combine M365LicensesDefault and M365LicensesAdditional to one array - const M365Licenses = [...M365LicensesDefault, ...M365LicensesAdditional]; let licenses = []; let missingSkuIds = []; @@ -24,17 +33,16 @@ export const getCippLicenseTranslation = (licenseArray) => { licenseArray?.forEach((licenseAssignment) => { let found = false; - // First, check static JSON files - for (let x = 0; x < M365Licenses.length; x++) { - if (licenseAssignment.skuId === M365Licenses[x].GUID) { - licenses.push( - M365Licenses[x].Product_Display_Name - ? M365Licenses[x].Product_Display_Name - : licenseAssignment.skuPartNumber, - ); - found = true; - break; - } + // First, check static JSON map (O(1) lookup) + const skuLower = licenseAssignment.skuId?.toLowerCase(); + const displayName = skuLower ? licenseByGuid.get(skuLower) : undefined; + if (displayName) { + licenses.push(displayName); + found = true; + } else if (skuLower && licenseByGuid.has(skuLower)) { + // Entry exists but Product_Display_Name is falsy — fall back to skuPartNumber + licenses.push(licenseAssignment.skuPartNumber || licenseAssignment.skuId); + found = true; } // Second, check dynamic cache From 69172aa1aafdcb06140a67a23e6250c53704c24a Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 14 Apr 2026 15:25:58 +0800 Subject: [PATCH 02/30] Normalize API function description handling add validation to app manifest templates --- .vscode/launch.json | 7 + .../AppApprovalTemplateForm.jsx | 145 +++++++++++++++--- .../CippAppApprovalTemplateDrawer.jsx | 2 +- .../CippSettings/CippPermissionCheck.jsx | 13 +- .../CippSettings/CippRoleAddEdit.jsx | 24 ++- src/utils/get-cipp-error.js | 2 +- 6 files changed, 170 insertions(+), 23 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 647f3571b4c2..3622dc676b7c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -35,6 +35,13 @@ "cwd": "${cwd}", "script": ". '${cwd}\\Tools\\Start-CIPPDevEmulators.ps1'" }, + { + "type": "PowerShell", + "name": "Launch in Windows Terminal with Offloading Proc and HTTP only workers", + "request": "launch", + "cwd": "${cwd}", + "script": ". '${cwd}\\Tools\\Start-CippOffloadSimulation.ps1'" + }, { "type": "PowerShell", "name": "Launch in Kitty Terminal", diff --git a/src/components/CippComponents/AppApprovalTemplateForm.jsx b/src/components/CippComponents/AppApprovalTemplateForm.jsx index e58add0e6945..20db0c4c48cd 100644 --- a/src/components/CippComponents/AppApprovalTemplateForm.jsx +++ b/src/components/CippComponents/AppApprovalTemplateForm.jsx @@ -19,9 +19,76 @@ const AppApprovalTemplateForm = ({ refetchKey, hideSubmitButton = false, // New prop to hide the submit button when used in a drawer }) => { + const forbiddenManifestProperties = ["keyCredentials", "passwordCredentials"]; const [selectedPermissionSet, setSelectedPermissionSet] = useState(null); const [permissionsLoaded, setPermissionsLoaded] = useState(false); const [permissionSetDrawerVisible, setPermissionSetDrawerVisible] = useState(false); + const [manifestSanitizeMessage, setManifestSanitizeMessage] = useState(null); + + const getManifestValidationError = (manifest) => { + if (!manifest.displayName) { + return "Application manifest must include a 'displayName' property"; + } + + if (manifest.signInAudience && manifest.signInAudience !== "AzureADMyOrg") { + return "signInAudience must be null, undefined, or 'AzureADMyOrg' for security reasons"; + } + + const presentForbiddenProperties = forbiddenManifestProperties.filter( + (propertyName) => Object.prototype.hasOwnProperty.call(manifest, propertyName) + ); + if (presentForbiddenProperties.length > 0) { + return `Remove unsupported manifest properties: ${presentForbiddenProperties.join(", ")}.`; + } + + return null; + }; + + const handleSanitizeManifest = () => { + const currentManifest = formControl.getValues("applicationManifest"); + + if (!currentManifest) { + setManifestSanitizeMessage({ + severity: "warning", + text: "Paste a manifest first, then use cleanup.", + }); + return; + } + + try { + const parsedManifest = JSON.parse(currentManifest); + const removedProperties = forbiddenManifestProperties.filter((propertyName) => + Object.prototype.hasOwnProperty.call(parsedManifest, propertyName) + ); + + if (removedProperties.length === 0) { + setManifestSanitizeMessage({ + severity: "info", + text: "No forbidden sections found. Your manifest is already clean.", + }); + return; + } + + removedProperties.forEach((propertyName) => { + delete parsedManifest[propertyName]; + }); + + formControl.setValue("applicationManifest", JSON.stringify(parsedManifest, null, 2), { + shouldDirty: true, + shouldValidate: true, + }); + + setManifestSanitizeMessage({ + severity: "success", + text: `Removed forbidden sections: ${removedProperties.join(", ")}.`, + }); + } catch (error) { + setManifestSanitizeMessage({ + severity: "error", + text: "Manifest JSON is invalid. Fix the JSON and try cleanup again.", + }); + } + }; // Watch for app type selection changes const selectedAppType = useWatch({ @@ -40,6 +107,28 @@ const AppApprovalTemplateForm = ({ name: "applicationManifest", }); + const getForbiddenManifestPropertiesPresent = (manifestValue) => { + if (!manifestValue) { + return []; + } + + try { + const manifest = JSON.parse(manifestValue); + return forbiddenManifestProperties.filter((propertyName) => + Object.prototype.hasOwnProperty.call(manifest, propertyName) + ); + } catch { + return []; + } + }; + + const forbiddenPropertiesInCurrentManifest = + selectedAppType === "ApplicationManifest" + ? getForbiddenManifestPropertiesPresent(selectedApplicationManifest) + : []; + const showSanitizeManifestButton = forbiddenPropertiesInCurrentManifest.length > 0; + const isTemplateFormValid = formControl?.formState?.isValid ?? false; + // Watch for app selection changes to update template name const selectedApp = useWatch({ control: formControl?.control, @@ -236,6 +325,22 @@ const AppApprovalTemplateForm = ({ } }, [isEditing, isCopy, templateData]); + useEffect(() => { + if (!formControl) { + return; + } + + formControl.trigger(); + }, [ + formControl, + selectedAppType, + selectedApplicationManifest, + selectedApp, + selectedGalleryTemplate, + selectedPermissionSetValue, + templateData, + ]); + // Handle form submission const handleSubmit = (data) => { let appDisplayName, appId, galleryTemplateId, applicationManifest; @@ -249,11 +354,12 @@ const AppApprovalTemplateForm = ({ try { applicationManifest = JSON.parse(data.applicationManifest); - // Validate signInAudience - only allow null/undefined or "AzureADMyOrg" - if ( - applicationManifest.signInAudience && - applicationManifest.signInAudience !== "AzureADMyOrg" - ) { + const manifestValidationError = getManifestValidationError(applicationManifest); + if (manifestValidationError) { + setManifestSanitizeMessage({ + severity: "error", + text: manifestValidationError, + }); return; // Don't submit if validation fails } @@ -481,24 +587,27 @@ const AppApprovalTemplateForm = ({ validate: (value) => { try { const manifest = JSON.parse(value); - - // Check for minimum required property - if (!manifest.displayName) { - return "Application manifest must include a 'displayName' property"; - } - - // Validate signInAudience if present - if (manifest.signInAudience && manifest.signInAudience !== "AzureADMyOrg") { - return "signInAudience must be null, undefined, or 'AzureADMyOrg' for security reasons"; - } - - return true; + return getManifestValidationError(manifest) ?? true; } catch (e) { return "Invalid JSON format"; } }, }} /> + + {showSanitizeManifestButton && ( + + + + )} + {manifestSanitizeMessage && ( + + {manifestSanitizeMessage.text} + + )} + {isEditing ? "Update Template" : "Create Template"} diff --git a/src/components/CippComponents/CippAppApprovalTemplateDrawer.jsx b/src/components/CippComponents/CippAppApprovalTemplateDrawer.jsx index 95526d194495..5315e080eb5a 100644 --- a/src/components/CippComponents/CippAppApprovalTemplateDrawer.jsx +++ b/src/components/CippComponents/CippAppApprovalTemplateDrawer.jsx @@ -144,7 +144,7 @@ export const CippAppApprovalTemplateDrawer = ({ variant="contained" color="primary" onClick={formControl.handleSubmit(handleSubmit)} - disabled={updatePermissions.isPending} + disabled={updatePermissions.isPending || !formControl.formState.isValid} > {updatePermissions.isPending ? isEditMode diff --git a/src/components/CippSettings/CippPermissionCheck.jsx b/src/components/CippSettings/CippPermissionCheck.jsx index 575f95cae5e1..ea5e20b8acc9 100644 --- a/src/components/CippSettings/CippPermissionCheck.jsx +++ b/src/components/CippSettings/CippPermissionCheck.jsx @@ -112,6 +112,17 @@ const CippPermissionCheck = (props) => { ); }; + const responseData = executeCheck?.error?.response?.data; + const responseText = + typeof responseData === "string" ? responseData : responseData ? JSON.stringify(responseData) : ""; + const shouldShowApiResponse = responseText.includes( + "Access to this CIPP API endpoint is not allowed", + ); + const checkErrorMessage = + shouldShowApiResponse + ? responseText + : `Failed to load ${type} check. Please try refreshing the page.`; + return ( <> { > {executeCheck.isError && !importReport && ( - Failed to load {type} check. Please try refreshing the page. + {checkErrorMessage} )} {(executeCheck.isSuccess || executeCheck.isLoading) && ( diff --git a/src/components/CippSettings/CippRoleAddEdit.jsx b/src/components/CippSettings/CippRoleAddEdit.jsx index 4c02bf71e673..757215cd0f49 100644 --- a/src/components/CippSettings/CippRoleAddEdit.jsx +++ b/src/components/CippSettings/CippRoleAddEdit.jsx @@ -103,6 +103,24 @@ export const CippRoleAddEdit = ({ selectedRole }) => { return regex.test(value); }; + const getFunctionDescriptionText = (description) => { + if (!description) return null; + + if (Array.isArray(description)) { + return description?.[0]?.Text || description?.[0]?.text || null; + } + + if (typeof description === "string") { + return description; + } + + if (typeof description === "object") { + return description?.Text || description?.text || null; + } + + return null; + }; + const getBaseRolePermissions = (role) => { const roleConfig = cippRoles[role]; if (!roleConfig) return {}; @@ -434,7 +452,7 @@ export const CippRoleAddEdit = ({ selectedRole }) => { const apiFunction = apiPermissions[cat][obj][type][api]; items.push({ name: apiFunction.Name, - description: apiFunction.Description?.[0]?.Text || null, + description: getFunctionDescriptionText(apiFunction.Description), }); } return ( @@ -593,7 +611,9 @@ export const CippRoleAddEdit = ({ selectedRole }) => { Object.keys(apiPermissions[cat][obj][type]).forEach( (apiKey) => { const apiFunction = apiPermissions[cat][obj][type][apiKey]; - const descriptionText = apiFunction.Description?.[0]?.Text; + const descriptionText = getFunctionDescriptionText( + apiFunction.Description + ); allEndpoints.push({ label: descriptionText ? `${apiFunction.Name} - ${descriptionText}` diff --git a/src/utils/get-cipp-error.js b/src/utils/get-cipp-error.js index 4b38df2af676..105ced73caf2 100644 --- a/src/utils/get-cipp-error.js +++ b/src/utils/get-cipp-error.js @@ -16,7 +16,7 @@ export const getCippError = (data) => { if (data.response?.data?.Results) { return data.response.data.Results; } - + if (data.response?.data) { return data.response.data; } From 3b7ad085d6268d3d42f99ac897d7067bcbd7c8c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:44:11 +0000 Subject: [PATCH 03/30] chore(deps): bump react-virtuoso from 4.18.3 to 4.18.5 Bumps [react-virtuoso](https://github.com/petyosi/react-virtuoso/tree/HEAD/packages/react-virtuoso) from 4.18.3 to 4.18.5. - [Release notes](https://github.com/petyosi/react-virtuoso/releases) - [Changelog](https://github.com/petyosi/react-virtuoso/blob/master/packages/react-virtuoso/CHANGELOG.md) - [Commits](https://github.com/petyosi/react-virtuoso/commits/react-virtuoso@4.18.5/packages/react-virtuoso) --- updated-dependencies: - dependency-name: react-virtuoso dependency-version: 4.18.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 35da2539bbdb..de3e62aacbe5 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "react-redux": "9.2.0", "react-syntax-highlighter": "^16.1.0", "react-time-ago": "^7.3.3", - "react-virtuoso": "^4.18.3", + "react-virtuoso": "^4.18.5", "react-window": "^2.2.7", "recharts": "^3.7.0", "redux": "5.0.1", diff --git a/yarn.lock b/yarn.lock index 052eb57111f7..ec2d2cfe536b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7130,10 +7130,10 @@ react-virtualized-auto-sizer@^1.0.26: resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz#e9470ef6a778dc4f1d5fd76305fa2d8b610c357a" integrity sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A== -react-virtuoso@^4.18.3: - version "4.18.3" - resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.18.3.tgz#12e69600c258bc6e6bd31c2516942ef08700deac" - integrity sha512-fLz/peHAx4Eu0DLHurFEEI7Y6n5CqEoxBh04rgJM9yMuOJah2a9zWg/MUOmZLcp7zuWYorXq5+5bf3IRgkNvWg== +react-virtuoso@^4.18.5: + version "4.18.5" + resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.18.5.tgz#450108e585c7a1124b995c7ea3cf367ed4857631" + integrity sha512-QDyNjyNEuurZG67SOmzYyxEkQYSyGmAMixOI6M15L/Q4CF39EgG+88y6DgZRo0q7rmy0HPx3Fj90I8/tPdnRCQ== react-window@^2.2.7: version "2.2.7" From e3529a6c3dfdd3b90be8128cf7595c7ee20d7348 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:44:28 +0000 Subject: [PATCH 04/30] chore(deps): bump @tiptap/react from 3.20.4 to 3.20.5 Bumps [@tiptap/react](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/react) from 3.20.4 to 3.20.5. - [Release notes](https://github.com/ueberdosis/tiptap/releases) - [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/react/CHANGELOG.md) - [Commits](https://github.com/ueberdosis/tiptap/commits/v3.20.5/packages/react) --- updated-dependencies: - dependency-name: "@tiptap/react" dependency-version: 3.20.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 35da2539bbdb..e0cea990c819 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@tiptap/extension-image": "^3.20.5", "@tiptap/extension-table": "^3.19.0", "@tiptap/pm": "^3.22.3", - "@tiptap/react": "^3.4.1", + "@tiptap/react": "^3.20.5", "@tiptap/starter-kit": "^3.20.5", "@uiw/react-json-view": "^2.0.0-alpha.41", "@vvo/tzdb": "^6.198.0", diff --git a/yarn.lock b/yarn.lock index 052eb57111f7..81dc3170d087 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2147,10 +2147,10 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-3.20.5.tgz#b40e8e43db3123c5dee9864931f7f9ad1b1e07dc" integrity sha512-hraiiWkF58n8Jy0Wl3OGwjCTrGWwZZxez/IlexrzKQ/nMFdjDpensZucWwu59zhAM9fqZwGSLDtCFuak03WKnA== -"@tiptap/extension-bubble-menu@^3.20.4": - version "3.20.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.20.4.tgz#e7e3c033a74d5dc67b1dac84346ee12e07d81b86" - integrity sha512-EXywPlI8wjPcAb8ozymgVhjtMjFrnhtoyNTy8ZcObdpUi5CdO9j892Y7aPbKe5hLhlDpvJk7rMfir4FFKEmfng== +"@tiptap/extension-bubble-menu@^3.20.5": + version "3.22.3" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.22.3.tgz#f94a3ac119b8576d040dc458d44b4200ad03e27b" + integrity sha512-Y6zQjh0ypDg32HWgICEvmPSKjGLr39k3aDxxt/H0uQEZSfw4smT0hxUyyyjVjx68C6t6MTnwdfz0hPI5lL68vQ== dependencies: "@floating-ui/dom" "^1.0.0" @@ -2179,10 +2179,10 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.20.5.tgz#ea810297825b009c357559e66f5fd76e91e8c940" integrity sha512-/lDG9OjvAv0ynmgFH17mt/GUeGT5bqu0iPW8JMgaRqlKawk+uUIv5SF5WkXS4SwxXih+hXdPEQD3PWZnxlQxAQ== -"@tiptap/extension-floating-menu@^3.20.4": - version "3.20.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.20.4.tgz#5b082cf500330f0a66f91a49c3a1a232038662a7" - integrity sha512-AaPTFhoO8DBIElJyd/RTVJjkctvJuL+GHURX0npbtTxXq5HXbebVwf2ARNR7jMd/GThsmBaNJiGxZg4A2oeDqQ== +"@tiptap/extension-floating-menu@^3.20.5": + version "3.22.3" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.22.3.tgz#c9a911b7784cb45d6f8e7260d77bf2015066e5a4" + integrity sha512-0f8b4KZ3XKai8GXWseIYJGdOfQr3evtFbBo3U08zy2aYzMMXWG0zEF7qe5/oiYp2aZ95edjjITnEceviTsZkIg== "@tiptap/extension-gapcursor@^3.20.5": version "3.20.5" @@ -2295,17 +2295,17 @@ prosemirror-transform "^1.10.2" prosemirror-view "^1.38.1" -"@tiptap/react@^3.4.1": - version "3.20.4" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-3.20.4.tgz#b313ff5051e30de4476138e47802a67bfda8ca2e" - integrity sha512-1B8iWsHWwb5TeyVaUs8BRPzwWo4PsLQcl03urHaz0zTJ8DauopqvxzV3+lem1OkzRHn7wnrapDvwmIGoROCaQw== +"@tiptap/react@^3.20.5": + version "3.20.5" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-3.20.5.tgz#6b2bd999470d9037fd9741ba598c9b3dc12f7759" + integrity sha512-in37o1Eo7JCflcHyK/SDfgkJBgX0LRN3LMk+NdLPTerRnC0zhGLQlpfBL4591TLTOUQde7QIrLv98smYO2mj+w== dependencies: "@types/use-sync-external-store" "^0.0.6" fast-equals "^5.3.3" use-sync-external-store "^1.4.0" optionalDependencies: - "@tiptap/extension-bubble-menu" "^3.20.4" - "@tiptap/extension-floating-menu" "^3.20.4" + "@tiptap/extension-bubble-menu" "^3.20.5" + "@tiptap/extension-floating-menu" "^3.20.5" "@tiptap/starter-kit@^3.20.5": version "3.20.5" From 9f68bf09d1795ce3965c3604b4e19767eef7f325 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:44:44 +0000 Subject: [PATCH 05/30] chore(deps): bump axios from 1.14.0 to 1.15.0 Bumps [axios](https://github.com/axios/axios) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 35da2539bbdb..d3437e124bad 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@uiw/react-json-view": "^2.0.0-alpha.41", "@vvo/tzdb": "^6.198.0", "apexcharts": "5.10.4", - "axios": "1.14.0", + "axios": "1.15.0", "date-fns": "4.1.0", "diff": "^8.0.3", "eml-parse-js": "^1.2.0-beta.0", diff --git a/yarn.lock b/yarn.lock index 052eb57111f7..e6d9782a43a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3041,10 +3041,10 @@ axe-core@^4.10.0: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.1.tgz#052ff9b2cbf543f5595028b583e4763b40c78ea7" integrity sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A== -axios@1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.14.0.tgz#7c29f4cf2ea91ef05018d5aa5399bf23ed3120eb" - integrity sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ== +axios@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f" + integrity sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q== dependencies: follow-redirects "^1.15.11" form-data "^4.0.5" From 17183631108d2c9a3c97fb538047c6ed85e4ab00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:44:54 +0000 Subject: [PATCH 06/30] chore(deps): bump @mui/x-date-pickers from 8.27.2 to 9.0.2 Bumps [@mui/x-date-pickers](https://github.com/mui/mui-x/tree/HEAD/packages/x-date-pickers) from 8.27.2 to 9.0.2. - [Release notes](https://github.com/mui/mui-x/releases) - [Changelog](https://github.com/mui/mui-x/blob/master/CHANGELOG.md) - [Commits](https://github.com/mui/mui-x/commits/v9.0.2/packages/x-date-pickers) --- updated-dependencies: - dependency-name: "@mui/x-date-pickers" dependency-version: 9.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 52 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 35da2539bbdb..a9beba99fde7 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@mui/lab": "7.0.0-beta.17", "@mui/material": "7.3.7", "@mui/system": "7.3.2", - "@mui/x-date-pickers": "^8.27.2", + "@mui/x-date-pickers": "^9.0.2", "@musement/iso-duration": "^1.0.0", "@nivo/core": "^0.99.0", "@nivo/sankey": "^0.99.0", diff --git a/yarn.lock b/yarn.lock index 052eb57111f7..977fffa6f8d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1466,7 +1466,26 @@ dependencies: "@babel/runtime" "^7.28.6" -"@mui/utils@^7.3.2", "@mui/utils@^7.3.5", "@mui/utils@^7.3.7", "@mui/utils@^7.3.9": +"@mui/types@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-9.0.0.tgz#92d8c64e72cb863ee59108cb20cc476d648a3ab9" + integrity sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg== + dependencies: + "@babel/runtime" "^7.29.2" + +"@mui/utils@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-9.0.0.tgz#25b563ccbf537feba5f89c37a00cb8e6eea45ad0" + integrity sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg== + dependencies: + "@babel/runtime" "^7.29.2" + "@mui/types" "^9.0.0" + "@types/prop-types" "^15.7.15" + clsx "^2.1.1" + prop-types "^15.8.1" + react-is "^19.2.4" + +"@mui/utils@^7.3.2", "@mui/utils@^7.3.7", "@mui/utils@^7.3.9": version "7.3.9" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.9.tgz#8af5093fc93c2e582fa3d047f561c7b690509bc2" integrity sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw== @@ -1478,26 +1497,26 @@ prop-types "^15.8.1" react-is "^19.2.3" -"@mui/x-date-pickers@^8.27.2": - version "8.27.2" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-8.27.2.tgz#5ada1fb3adffff3e0fd0fee7702fba7f770dca68" - integrity sha512-06LFkHFRXJ2O9DMXtWAA3kY0jpbL7XH8iqa8L5cBlN+8bRx/UVLKlZYlhGv06C88jF9kuZWY1bUgrv/EoY/2Ww== +"@mui/x-date-pickers@^9.0.2": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-9.0.2.tgz#ac601e4655ce3017185d652f9f91a2914bb6e63a" + integrity sha512-rnyc2wFPnprTS5i8Lq9aX2Rlx+ZRvNZERTd7sPMErf/8HnMCYzxwErITbd+TuImlvo2vaLF1gNynqT8AJMusYw== dependencies: - "@babel/runtime" "^7.28.4" - "@mui/utils" "^7.3.5" - "@mui/x-internals" "8.26.0" + "@babel/runtime" "^7.28.6" + "@mui/utils" "9.0.0" + "@mui/x-internals" "^9.0.0" "@types/react-transition-group" "^4.4.12" clsx "^2.1.1" prop-types "^15.8.1" react-transition-group "^4.4.5" -"@mui/x-internals@8.26.0": - version "8.26.0" - resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-8.26.0.tgz#49caacac954c29a1b10425c67418310ceb9c8cfa" - integrity sha512-B9OZau5IQUvIxwpJZhoFJKqRpmWf5r0yMmSXjQuqb5WuqM755EuzWJOenY48denGoENzMLT8hQpA0hRTeU2IPA== +"@mui/x-internals@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-9.0.0.tgz#8851a058e09b719690b4f398319805239e923855" + integrity sha512-E/4rdg69JjhyybpPGypCjAKSKLLnSdCFM+O6P/nkUg47+qt3uftxQEhjQO53rcn6ahHl6du/uNZ9BLgeY6kYxQ== dependencies: - "@babel/runtime" "^7.28.4" - "@mui/utils" "^7.3.5" + "@babel/runtime" "^7.28.6" + "@mui/utils" "9.0.0" reselect "^5.1.1" use-sync-external-store "^1.6.0" @@ -7010,6 +7029,11 @@ react-is@^19.2.3: resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== +react-is@^19.2.4: + version "19.2.5" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.5.tgz#7e7b54143e9313fed787b23fd4295d5a23872ad9" + integrity sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ== + react-leaflet-markercluster@^5.0.0-rc.0: version "5.0.0-rc.0" resolved "https://registry.yarnpkg.com/react-leaflet-markercluster/-/react-leaflet-markercluster-5.0.0-rc.0.tgz#42b1b9786de565fe69ec95abc6ff3232713f5300" From 9ce58d67df423b54a630a06b4c2b388becd29930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:45:02 +0000 Subject: [PATCH 07/30] chore(deps): bump actions/github-script from 8 to 9 Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v8...v9) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/auto_comments.yml | 2 +- .github/workflows/pr_check.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto_comments.yml b/.github/workflows/auto_comments.yml index 9a7664c9007c..89a9908a6748 100644 --- a/.github/workflows/auto_comments.yml +++ b/.github/workflows/auto_comments.yml @@ -17,7 +17,7 @@ jobs: # 1) If the comment includes '!notasponsor', delete it using GitHub Script - name: Delete !notasponsor comment if: contains(github.event.comment.body, '!notasponsor') - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml index 15b52ecd4e82..ea36ac1e82f7 100644 --- a/.github/workflows/pr_check.yml +++ b/.github/workflows/pr_check.yml @@ -25,7 +25,7 @@ jobs: github.event.pull_request.head.repo.fork == true && ((github.event.pull_request.head.ref == 'main' || github.event.pull_request.head.ref == 'master') || (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master')) - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | From aa62533e51a0b2d81434dc3614fcc1425b6ae850 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:02:48 +0200 Subject: [PATCH 08/30] fixes frontend loop if 500 is outside of app bounds --- src/pages/500.js | 60 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/src/pages/500.js b/src/pages/500.js index d25c3b9e761d..14f36c54343b 100644 --- a/src/pages/500.js +++ b/src/pages/500.js @@ -1,10 +1,58 @@ -import { Box, Container, Stack, Typography } from "@mui/material"; +import { Box, Button, Container, Stack, Typography } from "@mui/material"; import { Grid } from "@mui/system"; import Head from "next/head"; import { CippImageCard } from "../components/CippCards/CippImageCard.jsx"; import { Layout as DashboardLayout } from "../layouts/index.js"; import { useEffect } from "react"; import { useRouter } from "next/router.js"; +import { ErrorBoundary } from "react-error-boundary"; + +// Minimal fallback if DashboardLayout itself crashes — breaks the infinite loop +const MinimalErrorFallback = ({ error, resetErrorBoundary, outerError }) => { + const handleClearCacheAndReload = () => { + if (typeof window !== "undefined") { + Object.keys(localStorage).forEach((key) => { + if (key.startsWith("REACT_QUERY_OFFLINE_CACHE")) { + localStorage.removeItem(key); + } + }); + } + window.location.reload(true); + }; + + return ( + + + + Error 500 - Something went wrong + Oh no! It seems something went wrong. + + {outerError?.message || error?.message} + + + + + + + + + ); +}; const Error500 = (props) => { //when we browse away from the page we want to reset the error boundary @@ -17,7 +65,11 @@ const Error500 = (props) => { }, [router]); return ( - <> + ( + + )} + > 500 - Error @@ -45,7 +97,7 @@ const Error500 = (props) => { text={ <> Oh no! It seems something went wrong. -
{props.error.message}
+
{props.error?.message}
You can use the button below to try again. } @@ -59,7 +111,7 @@ const Error500 = (props) => {
- +
); }; From c4cba3acd5fda1b640ea5f4455a7ccb9ebfcd714 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:02:54 +0200 Subject: [PATCH 09/30] fixes frontend loop if 500 is outside of app bounds --- src/pages/500.js | 64 ++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/pages/500.js b/src/pages/500.js index 14f36c54343b..623609b2d974 100644 --- a/src/pages/500.js +++ b/src/pages/500.js @@ -1,33 +1,33 @@ -import { Box, Button, Container, Stack, Typography } from "@mui/material"; -import { Grid } from "@mui/system"; -import Head from "next/head"; -import { CippImageCard } from "../components/CippCards/CippImageCard.jsx"; -import { Layout as DashboardLayout } from "../layouts/index.js"; -import { useEffect } from "react"; -import { useRouter } from "next/router.js"; -import { ErrorBoundary } from "react-error-boundary"; +import { Box, Button, Container, Stack, Typography } from '@mui/material' +import { Grid } from '@mui/system' +import Head from 'next/head' +import { CippImageCard } from '../components/CippCards/CippImageCard.jsx' +import { Layout as DashboardLayout } from '../layouts/index.js' +import { useEffect } from 'react' +import { useRouter } from 'next/router.js' +import { ErrorBoundary } from 'react-error-boundary' // Minimal fallback if DashboardLayout itself crashes — breaks the infinite loop const MinimalErrorFallback = ({ error, resetErrorBoundary, outerError }) => { const handleClearCacheAndReload = () => { - if (typeof window !== "undefined") { + if (typeof window !== 'undefined') { Object.keys(localStorage).forEach((key) => { - if (key.startsWith("REACT_QUERY_OFFLINE_CACHE")) { - localStorage.removeItem(key); + if (key.startsWith('REACT_QUERY_OFFLINE_CACHE')) { + localStorage.removeItem(key) } - }); + }) } - window.location.reload(true); - }; + window.location.reload(true) + } return ( @@ -36,7 +36,7 @@ const MinimalErrorFallback = ({ error, resetErrorBoundary, outerError }) => { Oh no! It seems something went wrong. {outerError?.message || error?.message} @@ -51,18 +51,18 @@ const MinimalErrorFallback = ({ error, resetErrorBoundary, outerError }) => { - ); -}; + ) +} const Error500 = (props) => { //when we browse away from the page we want to reset the error boundary //this will prevent the error from showing on other pages - const router = useRouter(); + const router = useRouter() useEffect(() => { return () => { - props.resetErrorBoundary(); - }; - }, [router]); + props.resetErrorBoundary() + } + }, [router]) return ( { sx={{ flexGrow: 1, py: 4, - height: "80vh", + height: '80vh', }} > - + { } title="Error 500 - Something went wrong" - linkText={"Try again"} + linkText={'Try again'} onButtonClick={() => props.resetErrorBoundary()} /> @@ -112,7 +112,7 @@ const Error500 = (props) => { - ); -}; + ) +} -export default Error500; +export default Error500 From 1760b59e05bbc28931cdc32aa3c6179e766c6cd1 Mon Sep 17 00:00:00 2001 From: "Simon K. Hansen" <40685882+zenturash@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:14:20 +0200 Subject: [PATCH 10/30] feat: add AI Administrator role to GDAPRoles Add AI Administrator role to GDAPRoles Adds the AI Administrator privileged role (d2562ede-74db-457e-a7b6-544e236ebb61) for managing Microsoft 365 Copilot, AI-related enterprise services, and copilot agents. Signed-off-by: Simon K. Hansen <40685882+zenturash@users.noreply.github.com> --- src/data/GDAPRoles.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/data/GDAPRoles.json b/src/data/GDAPRoles.json index 7a92b58f95bc..df827501cdeb 100644 --- a/src/data/GDAPRoles.json +++ b/src/data/GDAPRoles.json @@ -814,5 +814,13 @@ "IsSystem": true, "Name": "Customer Delegated Admin Relationship Administrator", "ObjectId": "fc8ad4e2-40e4-4724-8317-bcda7503ecbf" + }, + { + "ExtensionData": {}, + "Description": "Assign the AI Administrator role to users who need to manage all aspects of Microsoft 365 Copilot, AI-related enterprise services, copilot agents, and view usage reports and service health dashboards.", + "IsEnabled": true, + "IsSystem": true, + "Name": "AI Administrator", + "ObjectId": "d2562ede-74db-457e-a7b6-544e236ebb61" } ] From 8cd33c87a34a16b14d9117862dbc5fe26adec232 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:41:33 +0800 Subject: [PATCH 11/30] Add custom scripts to backup --- src/components/CippComponents/CippRestoreWizard.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/CippComponents/CippRestoreWizard.jsx b/src/components/CippComponents/CippRestoreWizard.jsx index e2218d4d7e88..a539ccf16770 100644 --- a/src/components/CippComponents/CippRestoreWizard.jsx +++ b/src/components/CippComponents/CippRestoreWizard.jsx @@ -44,6 +44,7 @@ const TABLE_LABELS = { AppPermissions: "App Permissions", CommunityRepos: "Community Repositories", Config: "CIPP Configuration", + CustomPowershellScripts: "Custom PowerShell/Test Scripts", CustomData: "Custom Data", CustomRoles: "Custom Roles", Domains: "Domains", From 37a54d4eab1d96cbd724a4dee0a691e675724a3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:24:58 +0000 Subject: [PATCH 12/30] chore(deps): bump @mui/system from 7.3.2 to 7.3.10 Bumps [@mui/system](https://github.com/mui/material-ui/tree/HEAD/packages/mui-system) from 7.3.2 to 7.3.10. - [Release notes](https://github.com/mui/material-ui/releases) - [Changelog](https://github.com/mui/material-ui/blob/v7.3.10/CHANGELOG.md) - [Commits](https://github.com/mui/material-ui/commits/v7.3.10/packages/mui-system) --- updated-dependencies: - dependency-name: "@mui/system" dependency-version: 7.3.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 47 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 17691311015a..8ffb0e3bc1cc 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@mui/icons-material": "7.3.7", "@mui/lab": "7.0.0-beta.17", "@mui/material": "7.3.7", - "@mui/system": "7.3.2", + "@mui/system": "9.0.0", "@mui/x-date-pickers": "^9.0.2", "@musement/iso-duration": "^1.0.0", "@nivo/core": "^0.99.0", diff --git a/yarn.lock b/yarn.lock index a6d5289e57ce..7b54ea378494 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1410,7 +1410,7 @@ react-is "^19.2.3" react-transition-group "^4.4.5" -"@mui/private-theming@^7.3.2", "@mui/private-theming@^7.3.9": +"@mui/private-theming@^7.3.9": version "7.3.9" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.3.9.tgz#c785dc429b7ed62cf3952140be703cbe95704a13" integrity sha512-ErIyRQvsiQEq7Yvcvfw9UDHngaqjMy9P3JDPnRAaKG5qhpl2C4tX/W1S4zJvpu+feihmZJStjIyvnv6KDbIrlw== @@ -1419,7 +1419,16 @@ "@mui/utils" "^7.3.9" prop-types "^15.8.1" -"@mui/styled-engine@^7.3.2", "@mui/styled-engine@^7.3.9": +"@mui/private-theming@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-9.0.0.tgz#2617a4fa2045e0644429556afebe87aeaa0ea966" + integrity sha512-JtuZoaiCqwD6vjgYu6Xp3T7DZkrxJlgtDz5yESzhI34fEX5hHMh2VJUbuL9UOg8xrfIFMrq6dcYoH/7Zi4G0RA== + dependencies: + "@babel/runtime" "^7.29.2" + "@mui/utils" "^9.0.0" + prop-types "^15.8.1" + +"@mui/styled-engine@^7.3.9": version "7.3.9" resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.9.tgz#e425ca7b5cb559bde01b8fa4a7a842e9b5916f53" integrity sha512-JqujWt5bX4okjUPGpVof/7pvgClqh7HvIbsIBIOOlCh2u3wG/Bwp4+E1bc1dXSwkrkp9WUAoNdI5HEC+5HKvMw== @@ -1431,18 +1440,30 @@ csstype "^3.2.3" prop-types "^15.8.1" -"@mui/system@7.3.2": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.2.tgz#e838097fc6cb0a2e4c1822478950db89affb116a" - integrity sha512-9d8JEvZW+H6cVkaZ+FK56R53vkJe3HsTpcjMUtH8v1xK6Y1TjzHdZ7Jck02mGXJsE6MQGWVs3ogRHTQmS9Q/rA== +"@mui/styled-engine@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-9.0.0.tgz#ab7b1c25c0193a796ced29da3662191d53ae339b" + integrity sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg== dependencies: - "@babel/runtime" "^7.28.3" - "@mui/private-theming" "^7.3.2" - "@mui/styled-engine" "^7.3.2" - "@mui/types" "^7.4.6" - "@mui/utils" "^7.3.2" + "@babel/runtime" "^7.29.2" + "@emotion/cache" "^11.14.0" + "@emotion/serialize" "^1.3.3" + "@emotion/sheet" "^1.4.0" + csstype "^3.2.3" + prop-types "^15.8.1" + +"@mui/system@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-9.0.0.tgz#a95509127a7df264692105abebaf5a4a965401d5" + integrity sha512-YnC5Zg6j04IxiLc/boAKs0464jfZlLFVa7mf5E8lF0XOtZVUvG6R6gJK50lgUYdaaLdyLfxF6xR7LaPuEpeT/g== + dependencies: + "@babel/runtime" "^7.29.2" + "@mui/private-theming" "^9.0.0" + "@mui/styled-engine" "^9.0.0" + "@mui/types" "^9.0.0" + "@mui/utils" "^9.0.0" clsx "^2.1.1" - csstype "^3.1.3" + csstype "^3.2.3" prop-types "^15.8.1" "@mui/system@^7.3.2", "@mui/system@^7.3.7": @@ -1473,7 +1494,7 @@ dependencies: "@babel/runtime" "^7.29.2" -"@mui/utils@9.0.0": +"@mui/utils@9.0.0", "@mui/utils@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-9.0.0.tgz#25b563ccbf537feba5f89c37a00cb8e6eea45ad0" integrity sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg== From 6da156f17d9adefc6c2eef1da8236ddd9aa6e56f Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Sat, 18 Apr 2026 03:05:02 +0800 Subject: [PATCH 13/30] Endpoint changes --- .../CippWizard/CippGDAPTenantOnboarding.jsx | 6 +- src/pages/onboardingv2.js | 85 +++++++++++-------- src/pages/tenant/gdap-management/index.js | 82 +++++++++--------- .../tenant/gdap-management/offboarding.js | 55 ++++-------- .../gdap-management/onboarding/start.js | 11 +-- .../gdap-management/relationships/index.js | 14 +-- .../relationships/relationship/index.js | 2 +- .../relationships/relationship/mappings.js | 2 +- 8 files changed, 116 insertions(+), 141 deletions(-) diff --git a/src/components/CippWizard/CippGDAPTenantOnboarding.jsx b/src/components/CippWizard/CippGDAPTenantOnboarding.jsx index 4c5dc48a77cc..6357c5642eb5 100644 --- a/src/components/CippWizard/CippGDAPTenantOnboarding.jsx +++ b/src/components/CippWizard/CippGDAPTenantOnboarding.jsx @@ -45,11 +45,7 @@ export const CippGDAPTenantOnboarding = (props) => { }); const relationshipList = ApiGetCall({ - url: "/api/ListGraphRequest", - data: { - TenantFilter: "", - Endpoint: "tenantRelationships/delegatedAdminRelationships", - }, + url: "/api/ListGDAPRelationships", queryKey: "GDAPRelationshipOnboarding-wizard", }); diff --git a/src/pages/onboardingv2.js b/src/pages/onboardingv2.js index 59f101d6f8f8..3046798627ac 100644 --- a/src/pages/onboardingv2.js +++ b/src/pages/onboardingv2.js @@ -13,52 +13,64 @@ import { CippDirectTenantDeploy } from "../components/CippWizard/CippDirectTenan import { CippGDAPTenantSetup } from "../components/CippWizard/CippGDAPTenantSetup.jsx"; import { CippGDAPTenantOnboarding } from "../components/CippWizard/CippGDAPTenantOnboarding.jsx"; import { BuildingOfficeIcon, CloudIcon, CpuChipIcon } from "@heroicons/react/24/outline"; +import { useRouter } from "next/router"; const Page = () => { + const router = useRouter(); + const selectedOptionQuery = router.query?.selectedOption; + const deepLinkedOption = Array.isArray(selectedOptionQuery) + ? selectedOptionQuery[0] + : selectedOptionQuery; + const setupOptions = [ + { + description: + "Choose this option if this is your first setup, or if you'd like to redo the previous setup.", + icon: , + label: "First Setup", + value: "FirstSetup", + }, + { + description: "Choose this option if you would like to add a tenant to your environment.", + icon: , + label: "Add a tenant", + value: "AddTenant", + }, + { + description: + "Choose this option if you want to setup which application registration is used to connect to your tenants.", + icon: , + label: "Create a new application registration for me and connect to my tenants", + value: "CreateApp", + }, + { + description: "I would like to refresh my token or replace the account I've used.", + icon: , + label: "Refresh Tokens for existing application registration", + value: "UpdateTokens", + }, + { + description: + "I have an existing application and would like to manually enter my token, or update them. This is only recommended for advanced users.", + icon: , + label: "Manually enter credentials", + value: "Manual", + }, + ]; + + const hasDeepLinkedOption = + typeof deepLinkedOption === "string" && + setupOptions.some((option) => option.value === deepLinkedOption); + const steps = [ { description: "Onboarding", component: CippWizardOptionsList, + hideStepWhen: () => hasDeepLinkedOption, componentProps: { title: "Select your setup method", subtext: `This wizard will guide you through setting up CIPPs access to your client tenants. If this is your first time setting up CIPP you will want to choose the option "Create application for me and connect to my tenants",`, valuesKey: "SyncTool", - options: [ - { - description: - "Choose this option if this is your first setup, or if you'd like to redo the previous setup.", - icon: , - label: "First Setup", - value: "FirstSetup", - }, - { - description: - "Choose this option if you would like to add a tenant to your environment.", - icon: , - label: "Add a tenant", - value: "AddTenant", - }, - { - description: - "Choose this option if you want to setup which application registration is used to connect to your tenants.", - icon: , - label: "Create a new application registration for me and connect to my tenants", - value: "CreateApp", - }, - { - description: "I would like to refresh my token or replace the account I've used.", - icon: , - label: "Refresh Tokens for existing application registration", - value: "UpdateTokens", - }, - { - description: - "I have an existing application and would like to manually enter my token, or update them. This is only recommended for advanced users.", - icon: , - label: "Manually enter credentials", - value: "Manual", - }, - ], + options: setupOptions, }, }, { @@ -137,6 +149,7 @@ const Page = () => { steps={steps} wizardTitle="Setup Wizard" postUrl={"/api/ExecCombinedSetup"} + initialState={hasDeepLinkedOption ? { selectedOption: deepLinkedOption } : undefined} /> ); diff --git a/src/pages/tenant/gdap-management/index.js b/src/pages/tenant/gdap-management/index.js index 9aafd4a40531..25e1a1308489 100644 --- a/src/pages/tenant/gdap-management/index.js +++ b/src/pages/tenant/gdap-management/index.js @@ -19,18 +19,16 @@ import CippButtonCard from "../../../components/CippCards/CippButtonCard"; import { WizardSteps } from "../../../components/CippWizard/wizard-steps"; import Link from "next/link"; import { CippHead } from "../../../components/CippComponents/CippHead"; +import { usePermissions } from "../../../hooks/use-permissions"; const Page = () => { const [createDefaults, setCreateDefaults] = useState(false); const [activeStep, setActiveStep] = useState(0); + const { checkRoles } = usePermissions(); + const canViewGdapChecks = checkRoles(["CIPP.AppSettings.Read"]); const relationships = ApiGetCallWithPagination({ - url: "/api/ListGraphRequest", - data: { - Endpoint: "tenantRelationships/delegatedAdminRelationships", - tenantFilter: "", - $top: 300, - }, + url: "/api/ListGDAPRelationships", queryKey: "ListGDAPRelationships", }); @@ -167,46 +165,50 @@ const Page = () => { - - - - - - - - + {canViewGdapChecks && ( + <> + + + + + + + + + + )} ); diff --git a/src/pages/tenant/gdap-management/offboarding.js b/src/pages/tenant/gdap-management/offboarding.js index d9486f775c25..aa6cddc87d0d 100644 --- a/src/pages/tenant/gdap-management/offboarding.js +++ b/src/pages/tenant/gdap-management/offboarding.js @@ -22,60 +22,46 @@ const Page = () => { return vendor.vendorTenantId; }) .join(","); - const vendorGraphFilter = `appOwnerOrganizationId in (${vendorFilter})`; const tenantId = useWatch({ control: formControl.control, name: "tenantFilter", }); const gdapRelationships = ApiGetCall({ - url: "/api/ListGraphRequest", - data: { - Endpoint: "tenantRelationships/delegatedAdminRelationships", - tenantFilter: "", - $top: 300, - }, + url: "/api/ListGDAPRelationships", queryKey: "ListGDAPRelationship", }); const cspContracts = ApiGetCall({ - url: "/api/ListGraphRequest", - data: { - Endpoint: "contracts", - tenantFilter: "", - $top: 300, - }, + url: "/api/ListGDAPContracts", queryKey: "ListContracts", }); const mspApps = ApiGetCall({ - url: "/api/ListGraphRequest", + url: "/api/ListGDAPServicePrincipals", data: { - Endpoint: "servicePrincipals", - TenantFilter: tenantId?.value, - $filter: `appOwnerOrganizationId eq %partnertenantid%`, - $select: "id,displayName,appId,appOwnerOrganizationId", - $count: true, + tenantFilter: tenantId?.value, + ownerType: "partner", }, queryKey: "ListMSPApps-" + tenantId?.value, + waiting: Boolean(tenantId?.value), }); const vendorApps = ApiGetCallWithPagination({ - url: "/api/ListGraphRequest", + url: "/api/ListGDAPServicePrincipals", data: { - Endpoint: "servicePrincipals", - TenantFilter: tenantId?.value, - $filter: vendorGraphFilter, - $select: "id,displayName,appId,appOwnerOrganizationId", - $count: true, + tenantFilter: tenantId?.value, + ownerType: "vendor", + vendorTenantIds: vendorFilter, }, queryKey: "ListVendorApps-" + tenantId?.value, + waiting: Boolean(tenantId?.value), }); return ( <> { label="Select Tenant to Offboard" type="autoComplete" api={{ - url: "/api/ExecExcludeTenant", - data: { - ListAll: true, - }, - queryKey: "ListAllTenants", + url: "/api/ListOffboardTenants", + queryKey: "ListOffboardTenants", labelField: (tenant) => { return `${tenant.displayName} (${tenant.defaultDomainName})`; }, @@ -205,13 +188,11 @@ const Page = () => { label="Vendor Applications to Remove" type="autoComplete" api={{ - url: "/api/ListGraphRequest", + url: "/api/ListGDAPServicePrincipals", data: { - Endpoint: "servicePrincipals", - TenantFilter: tenantId.value, - $filter: vendorGraphFilter, - $select: "id,displayName,appId,appOwnerOrganizationId", - $count: true, + tenantFilter: tenantId.value, + ownerType: "vendor", + vendorTenantIds: vendorFilter, }, dataKey: "Results", queryKey: "ListVendorApps-" + tenantId.value, diff --git a/src/pages/tenant/gdap-management/onboarding/start.js b/src/pages/tenant/gdap-management/onboarding/start.js index c2908d22a467..c5cf9cf5da3a 100644 --- a/src/pages/tenant/gdap-management/onboarding/start.js +++ b/src/pages/tenant/gdap-management/onboarding/start.js @@ -49,11 +49,7 @@ const Page = () => { }); const relationshipList = ApiGetCall({ - url: "/api/ListGraphRequest", - data: { - TenantFilter: "", - Endpoint: "tenantRelationships/delegatedAdminRelationships", - }, + url: "/api/ListGDAPRelationships", queryKey: "GDAPRelationshipOnboarding", }); const onboardingList = ApiGetCallWithPagination({ @@ -317,10 +313,7 @@ const Page = () => { label="Select GDAP Relationship" type="autoComplete" api={{ - url: "/api/ListGraphRequest", - data: { - Endpoint: "tenantRelationships/delegatedAdminRelationships", - }, + url: "/api/ListGDAPRelationships", excludeTenantFilter: true, queryKey: "GDAPRelationships", dataKey: "Results", diff --git a/src/pages/tenant/gdap-management/relationships/index.js b/src/pages/tenant/gdap-management/relationships/index.js index 8c3c3da01bc0..f3a3e4f441a7 100644 --- a/src/pages/tenant/gdap-management/relationships/index.js +++ b/src/pages/tenant/gdap-management/relationships/index.js @@ -4,8 +4,6 @@ import tabOptions from "../tabOptions"; import CippTablePage from "../../../../components/CippComponents/CippTablePage"; import CippGdapActions from "../../../../components/CippComponents/CippGdapActions"; -const pageTitle = "GDAP Relationships"; - const actions = CippGdapActions(); const simpleColumns = [ @@ -47,20 +45,12 @@ const offCanvas = { extendedInfoFields: simpleColumns, }; -const apiUrl = "/api/ListGraphRequest"; -const apiData = { - Endpoint: "tenantRelationships/delegatedAdminRelationships", - tenantFilter: "", - $top: 300, -}; - const Page = () => { return ( { const [relationshipData, setRelationshipData] = useState({}); const relationshipRequest = ApiGetCall({ - url: `/api/ListGraphRequest?Endpoint=tenantRelationships/delegatedAdminRelationships/${id}`, + url: `/api/ListGDAPRelationships?id=${id}`, queryKey: `ListRelationships-${id}`, }); diff --git a/src/pages/tenant/gdap-management/relationships/relationship/mappings.js b/src/pages/tenant/gdap-management/relationships/relationship/mappings.js index 3ec708b70c66..b9669e3f725f 100644 --- a/src/pages/tenant/gdap-management/relationships/relationship/mappings.js +++ b/src/pages/tenant/gdap-management/relationships/relationship/mappings.js @@ -12,7 +12,7 @@ const Page = () => { const { id } = router.query; const relationshipRequest = ApiGetCall({ - url: `/api/ListGraphRequest?Endpoint=tenantRelationships/delegatedAdminRelationships/${id}`, + url: `/api/ListGDAPRelationships?id=${id}`, queryKey: `ListRelationships-${id}`, }); From 007ab7132e8d36168ceccb32f36797c1bf1e6e10 Mon Sep 17 00:00:00 2001 From: Bobby <31723128+kris6673@users.noreply.github.com> Date: Sun, 19 Apr 2026 14:10:09 +0200 Subject: [PATCH 14/30] chore(deps): update/downgrade @mui packages to version 7.3.10 --- package.json | 6 +-- yarn.lock | 110 ++++++++++++++++++++++++++++----------------------- 2 files changed, 64 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 8ffb0e3bc1cc..07dae7f34412 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,10 @@ "@emotion/styled": "11.14.1", "@heroicons/react": "2.2.0", "@monaco-editor/react": "^4.6.0", - "@mui/icons-material": "7.3.7", + "@mui/icons-material": "7.3.10", "@mui/lab": "7.0.0-beta.17", - "@mui/material": "7.3.7", - "@mui/system": "9.0.0", + "@mui/material": "7.3.10", + "@mui/system": "7.3.10", "@mui/x-date-pickers": "^9.0.2", "@musement/iso-duration": "^1.0.0", "@nivo/core": "^0.99.0", diff --git a/yarn.lock b/yarn.lock index 7b54ea378494..717cf1e70089 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1368,17 +1368,17 @@ dependencies: "@monaco-editor/loader" "^1.5.0" -"@mui/core-downloads-tracker@^7.3.7": - version "7.3.9" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.9.tgz#d944e385f8f7f5e680e5ba479b39ff8602bd4939" - integrity sha512-MOkOCTfbMJwLshlBCKJ59V2F/uaLYfmKnN76kksj6jlGUVdI25A9Hzs08m+zjBRdLv+sK7Rqdsefe8X7h/6PCw== +"@mui/core-downloads-tracker@^7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.10.tgz#f6af0bbfa825f11849f5f190d984d6f8d5c0d961" + integrity sha512-vrOpWRmPJSuwLo23J62wggEm/jvGdzqctej+UOCtgDUz6nZJQuj3ByPccVyaa7eQmwAzUwKN56FQPMKkqbj1GA== -"@mui/icons-material@7.3.7": - version "7.3.7" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-7.3.7.tgz#01a6019c552e27c7f8a3451bcb47171ede8b34ac" - integrity sha512-3Q+ulAqG+A1+R4ebgoIs7AccaJhIGy+Xi/9OnvX376jQ6wcy+rz4geDGrxQxCGzdjOQr4Z3NgyFSZCz4T999lA== +"@mui/icons-material@7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-7.3.10.tgz#f0d232ebe007b3a125a52c5c9e1bece43a83b57c" + integrity sha512-Au0ma4NSKGKNiimukj8UT/W1x2Qx6Qwn2RvFGykiSqVLYBNlIOPbjnIMvrwLGLu89EEpTVdu/ys/OduZR+tWqw== dependencies: - "@babel/runtime" "^7.28.4" + "@babel/runtime" "^7.28.6" "@mui/lab@7.0.0-beta.17": version "7.0.0-beta.17" @@ -1392,16 +1392,16 @@ clsx "^2.1.1" prop-types "^15.8.1" -"@mui/material@7.3.7": - version "7.3.7" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-7.3.7.tgz#50fc9b9f8645a4d26a48d7c5f7fa0c9876a8c679" - integrity sha512-6bdIxqzeOtBAj2wAsfhWCYyMKPLkRO9u/2o5yexcL0C3APqyy91iGSWgT3H7hg+zR2XgE61+WAu12wXPON8b6A== +"@mui/material@7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-7.3.10.tgz#1c954b0e7aba220703afe07594388a69383c7363" + integrity sha512-cHvGOk2ZEfbQt3LnGe0ZKd/ETs9gsUpkW66DCO+GSjMZhpdKU4XsuIr7zJ/B/2XaN8ihxuzHfYAR4zPtCN4RYg== dependencies: - "@babel/runtime" "^7.28.4" - "@mui/core-downloads-tracker" "^7.3.7" - "@mui/system" "^7.3.7" - "@mui/types" "^7.4.10" - "@mui/utils" "^7.3.7" + "@babel/runtime" "^7.28.6" + "@mui/core-downloads-tracker" "^7.3.10" + "@mui/system" "^7.3.10" + "@mui/types" "^7.4.12" + "@mui/utils" "^7.3.10" "@popperjs/core" "^2.11.8" "@types/react-transition-group" "^4.4.12" clsx "^2.1.1" @@ -1410,6 +1410,15 @@ react-is "^19.2.3" react-transition-group "^4.4.5" +"@mui/private-theming@^7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.3.10.tgz#2e500959f39c7b305a30ff3f947f47dea9e8eac1" + integrity sha512-j3EZN+zOctxUISvJSmsEPo5o2F8zse4l5vRkBY+ps6UtnL6J7o14kUaI4w7gwo73id9e3cDNMVQK/9BVaMHVBw== + dependencies: + "@babel/runtime" "^7.28.6" + "@mui/utils" "^7.3.10" + prop-types "^15.8.1" + "@mui/private-theming@^7.3.9": version "7.3.9" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.3.9.tgz#c785dc429b7ed62cf3952140be703cbe95704a13" @@ -1419,19 +1428,10 @@ "@mui/utils" "^7.3.9" prop-types "^15.8.1" -"@mui/private-theming@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-9.0.0.tgz#2617a4fa2045e0644429556afebe87aeaa0ea966" - integrity sha512-JtuZoaiCqwD6vjgYu6Xp3T7DZkrxJlgtDz5yESzhI34fEX5hHMh2VJUbuL9UOg8xrfIFMrq6dcYoH/7Zi4G0RA== - dependencies: - "@babel/runtime" "^7.29.2" - "@mui/utils" "^9.0.0" - prop-types "^15.8.1" - -"@mui/styled-engine@^7.3.9": - version "7.3.9" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.9.tgz#e425ca7b5cb559bde01b8fa4a7a842e9b5916f53" - integrity sha512-JqujWt5bX4okjUPGpVof/7pvgClqh7HvIbsIBIOOlCh2u3wG/Bwp4+E1bc1dXSwkrkp9WUAoNdI5HEC+5HKvMw== +"@mui/styled-engine@^7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.10.tgz#53e98c1fdeda972b5932c76f6a2a29faf33f0d11" + integrity sha512-WxE9SiF8xskAQqGjsp0poXCkCqsoXFEsSr0HBXfApmGHR+DBnXRp+z46Vsltg4gpPM4Z96DeAQRpeAOnhNg7Ng== dependencies: "@babel/runtime" "^7.28.6" "@emotion/cache" "^11.14.0" @@ -1440,33 +1440,33 @@ csstype "^3.2.3" prop-types "^15.8.1" -"@mui/styled-engine@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-9.0.0.tgz#ab7b1c25c0193a796ced29da3662191d53ae339b" - integrity sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg== +"@mui/styled-engine@^7.3.9": + version "7.3.9" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.9.tgz#e425ca7b5cb559bde01b8fa4a7a842e9b5916f53" + integrity sha512-JqujWt5bX4okjUPGpVof/7pvgClqh7HvIbsIBIOOlCh2u3wG/Bwp4+E1bc1dXSwkrkp9WUAoNdI5HEC+5HKvMw== dependencies: - "@babel/runtime" "^7.29.2" + "@babel/runtime" "^7.28.6" "@emotion/cache" "^11.14.0" "@emotion/serialize" "^1.3.3" "@emotion/sheet" "^1.4.0" csstype "^3.2.3" prop-types "^15.8.1" -"@mui/system@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-9.0.0.tgz#a95509127a7df264692105abebaf5a4a965401d5" - integrity sha512-YnC5Zg6j04IxiLc/boAKs0464jfZlLFVa7mf5E8lF0XOtZVUvG6R6gJK50lgUYdaaLdyLfxF6xR7LaPuEpeT/g== +"@mui/system@7.3.10", "@mui/system@^7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.10.tgz#b8e668537605413be168a865d4e980c7d6747320" + integrity sha512-/sfPpdpJaQn7BSF+avjIdHSYmxHp0UOBYNxSG9QGKfMOD6sLANCpRPCnanq1Pe0lFf0NHkO2iUk0TNzdWC1USQ== dependencies: - "@babel/runtime" "^7.29.2" - "@mui/private-theming" "^9.0.0" - "@mui/styled-engine" "^9.0.0" - "@mui/types" "^9.0.0" - "@mui/utils" "^9.0.0" + "@babel/runtime" "^7.28.6" + "@mui/private-theming" "^7.3.10" + "@mui/styled-engine" "^7.3.10" + "@mui/types" "^7.4.12" + "@mui/utils" "^7.3.10" clsx "^2.1.1" csstype "^3.2.3" prop-types "^15.8.1" -"@mui/system@^7.3.2", "@mui/system@^7.3.7": +"@mui/system@^7.3.2": version "7.3.9" resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.9.tgz#d8181dd9ad8c5e9afdf50eb7009062c506976ab1" integrity sha512-aL1q9am8XpRrSabv9qWf5RHhJICJql34wnrc1nz0MuOglPRYF/liN+c8VqZdTvUn9qg+ZjRVbKf4sJVFfIDtmg== @@ -1480,7 +1480,7 @@ csstype "^3.2.3" prop-types "^15.8.1" -"@mui/types@^7.4.10", "@mui/types@^7.4.12", "@mui/types@^7.4.6": +"@mui/types@^7.4.12", "@mui/types@^7.4.6": version "7.4.12" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.12.tgz#e4eba37a7506419ea5c5e0604322ba82b271bf46" integrity sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w== @@ -1494,7 +1494,7 @@ dependencies: "@babel/runtime" "^7.29.2" -"@mui/utils@9.0.0", "@mui/utils@^9.0.0": +"@mui/utils@9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-9.0.0.tgz#25b563ccbf537feba5f89c37a00cb8e6eea45ad0" integrity sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg== @@ -1506,7 +1506,19 @@ prop-types "^15.8.1" react-is "^19.2.4" -"@mui/utils@^7.3.2", "@mui/utils@^7.3.7", "@mui/utils@^7.3.9": +"@mui/utils@^7.3.10": + version "7.3.10" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.10.tgz#b74050131ca9022c0815d16f54f1a6d757ab343d" + integrity sha512-7y2eIfy0h7JPz+Yy4pS+wgV68d46PuuxDqKBN4Q8VlPQSsCAGwroMCV6xWyc7g9dvEp8ZNFsknc59GHWO+r6Ow== + dependencies: + "@babel/runtime" "^7.28.6" + "@mui/types" "^7.4.12" + "@types/prop-types" "^15.7.15" + clsx "^2.1.1" + prop-types "^15.8.1" + react-is "^19.2.3" + +"@mui/utils@^7.3.2", "@mui/utils@^7.3.9": version "7.3.9" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.9.tgz#8af5093fc93c2e582fa3d047f561c7b690509bc2" integrity sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw== From 4520067ddaf02d506209e9e0a6e079e8ece0ffd3 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Mon, 20 Apr 2026 14:59:37 +0800 Subject: [PATCH 15/30] change domain validation to only 1 domain segment --- src/utils/get-cipp-validator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/get-cipp-validator.js b/src/utils/get-cipp-validator.js index e1db092ae1ad..f5541e0dc25c 100644 --- a/src/utils/get-cipp-validator.js +++ b/src/utils/get-cipp-validator.js @@ -41,7 +41,7 @@ export const getCippValidator = (value, type) => { case "wildcardDomain": return /^(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.\*)?$/.test(value) || /^(\*)?[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\*)?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/.test(value) || "This is not a valid domain pattern"; case "wildcardUrl": - return /^(https?:\/\/)?(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.\*)?([\/\?\*][a-zA-Z0-9\-\.\~\*\/\?=&%]*)?$/.test(value) || "This is not a valid URL pattern"; + return /^(https?:\/\/)?(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.\*)?([\/\?\*][a-zA-Z0-9\-\.\~\*\/\?=&%]*)?$/.test(value) || "This is not a valid URL pattern"; case "senderEntry": return ( /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/.test(value) || From 552757246e0a4268e8792a4bb8944d29b9136d30 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:22:49 +0800 Subject: [PATCH 16/30] fixes launching emulator --- Tools/Start-CippDevEmulators.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Tools/Start-CippDevEmulators.ps1 b/Tools/Start-CippDevEmulators.ps1 index 2b5d4a4a7ac6..45ac7598e959 100644 --- a/Tools/Start-CippDevEmulators.ps1 +++ b/Tools/Start-CippDevEmulators.ps1 @@ -42,5 +42,11 @@ try { $frontendCommand = 'try { npm run dev } catch { Write-Error $_.Exception.Message } finally { Read-Host "Press Enter to exit" }' $swaCommand = 'try { npm run start-swa } catch { Write-Error $_.Exception.Message } finally { Read-Host "Press Enter to exit" }' +# Encode commands to avoid parsing issues with multi-line strings +$azuriteEncoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($azuriteCommand)) +$apiEncoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($apiCommand)) +$frontendEncoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($frontendCommand)) +$swaEncoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($swaCommand)) + # Start Windows Terminal with all tabs -wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c $azuriteCommand`; new-tab --title 'FunctionApp' -d $ApiPath pwsh -c $apiCommand`; new-tab --title 'CIPP Frontend' -d $FrontendPath pwsh -c $frontendCommand`; new-tab --title 'SWA' -d $FrontendPath pwsh -c $swaCommand +wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -EncodedCommand $azuriteEncoded`; new-tab --title 'FunctionApp' -d $ApiPath pwsh -EncodedCommand $apiEncoded`; new-tab --title 'CIPP Frontend' -d $FrontendPath pwsh -EncodedCommand $frontendEncoded`; new-tab --title 'SWA' -d $FrontendPath pwsh -EncodedCommand $swaEncoded From 0bf17bd67c720dab499f9134958ac9083d864822 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:37:55 +0800 Subject: [PATCH 17/30] Update Start-CippDevEmulators.ps1 --- Tools/Start-CippDevEmulators.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/Start-CippDevEmulators.ps1 b/Tools/Start-CippDevEmulators.ps1 index 45ac7598e959..ecf6855fe1e5 100644 --- a/Tools/Start-CippDevEmulators.ps1 +++ b/Tools/Start-CippDevEmulators.ps1 @@ -22,7 +22,7 @@ $apiCommand = @' try { # Use a stable local identity so timer node selection treats this as the catch-all host. $env:WEBSITE_SITE_NAME = "cipp" - $env:CIPP_PROCESSOR = "true" + $env:CIPP_PROCESSOR = "false" $env:AzureFunctionsWebHost__hostid = "cipp-single" # Ensure prior offload simulation env overrides do not disable triggers in this shell. From 823b0f12677cd9dbcd2b5b3874472cebb7fccc34 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:49:59 +0800 Subject: [PATCH 18/30] Add standards required license sections (based on script licence checks) --- src/data/standards.json | 3621 ++++++++------------------------------- 1 file changed, 756 insertions(+), 2865 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index 0ede8237c3bf..9c6577c64b99 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -7,30 +7,10 @@ "docsDescription": "", "executiveText": "Establishes designated contact email addresses for receiving important Microsoft 365 subscription updates and notifications. This ensures proper communication channels are maintained for general, security, marketing, and technical matters, improving organizational responsiveness to critical system updates.", "addedComponent": [ - { - "type": "textField", - "name": "standards.MailContacts.GeneralContact", - "label": "General Contact", - "required": false - }, - { - "type": "textField", - "name": "standards.MailContacts.SecurityContact", - "label": "Security Contact", - "required": false - }, - { - "type": "textField", - "name": "standards.MailContacts.MarketingContact", - "label": "Marketing Contact", - "required": false - }, - { - "type": "textField", - "name": "standards.MailContacts.TechContact", - "label": "Technical Contact", - "required": false - } + { "type": "textField", "name": "standards.MailContacts.GeneralContact", "label": "General Contact", "required": false }, + { "type": "textField", "name": "standards.MailContacts.SecurityContact", "label": "Security Contact", "required": false }, + { "type": "textField", "name": "standards.MailContacts.MarketingContact", "label": "Marketing Contact", "required": false }, + { "type": "textField", "name": "standards.MailContacts.TechContact", "label": "Technical Contact", "required": false } ], "label": "Set contact e-mails", "impact": "Low Impact", @@ -47,37 +27,18 @@ "docsDescription": "This standard creates a new mail contact in Exchange Online. Mail contacts are useful for adding external email addresses to your organization's address book. They can be used for distribution lists, shared mailboxes, and other collaboration scenarios.", "executiveText": "Automatically creates external email contacts in the organization's address book, enabling seamless communication with external partners and vendors. This standardizes contact management across all company locations and improves collaboration efficiency.", "addedComponent": [ - { - "type": "textField", - "name": "standards.DeployMailContact.ExternalEmailAddress", - "label": "External Email Address", - "required": true - }, - { - "type": "textField", - "name": "standards.DeployMailContact.DisplayName", - "label": "Display Name", - "required": true - }, - { - "type": "textField", - "name": "standards.DeployMailContact.FirstName", - "label": "First Name", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployMailContact.LastName", - "label": "Last Name", - "required": false - } + { "type": "textField", "name": "standards.DeployMailContact.ExternalEmailAddress", "label": "External Email Address", "required": true }, + { "type": "textField", "name": "standards.DeployMailContact.DisplayName", "label": "Display Name", "required": true }, + { "type": "textField", "name": "standards.DeployMailContact.FirstName", "label": "First Name", "required": false }, + { "type": "textField", "name": "standards.DeployMailContact.LastName", "label": "Last Name", "required": false } ], "label": "Deploy Mail Contact", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-03-19", "powershellEquivalent": "New-MailContact", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DeployContactTemplates", @@ -93,36 +54,22 @@ "creatable": false, "label": "Select Mail Contact Templates", "name": "standards.DeployContactTemplates.templateIds", - "api": { - "url": "/api/ListContactTemplates", - "labelField": "name", - "valueField": "GUID", - "queryKey": "Contact Templates" - } + "api": { "url": "/api/ListContactTemplates", "labelField": "name", "valueField": "GUID", "queryKey": "Contact Templates" } } ], "label": "Deploy Mail Contact Template", - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-05-31", "powershellEquivalent": "New-MailContact", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AuditLog", "cat": "Global Standards", - "tag": [ - "CIS M365 5.0 (3.1.1)", - "mip_search_auditlog", - "NIST CSF 2.0 (DE.CM-09)", - "CISAMSEXO171", - "CISAMSEXO173" - ], + "tag": ["CIS M365 5.0 (3.1.1)", "mip_search_auditlog", "NIST CSF 2.0 (DE.CM-09)", "CISAMSEXO171", "CISAMSEXO173"], "helpText": "Enables the Unified Audit Log for tracking and auditing activities. Also runs Enable-OrganizationCustomization if necessary.", "executiveText": "Activates comprehensive activity logging across Microsoft 365 services to track user actions, system changes, and security events. This provides essential audit trails for compliance requirements, security investigations, and regulatory reporting.", "addedComponent": [], @@ -131,7 +78,8 @@ "impactColour": "info", "addedDate": "2021-11-16", "powershellEquivalent": "Enable-OrganizationCustomization", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.RestrictThirdPartyStorageServices", @@ -146,7 +94,8 @@ "impactColour": "warning", "addedDate": "2025-06-06", "powershellEquivalent": "New-MgServicePrincipal and Update-MgServicePrincipal", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.ProfilePhotos", @@ -163,14 +112,8 @@ "label": "Select value", "name": "standards.ProfilePhotos.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] } ], @@ -179,7 +122,8 @@ "impactColour": "info", "addedDate": "2025-01-19", "powershellEquivalent": "Set-OrganizationConfig -ProfilePhotoOptions EnablePhotos and Update-MgBetaAdminPeople", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.PhishProtection", @@ -192,13 +136,10 @@ "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-01-22", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "powershellEquivalent": "Portal only", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["AAD_PREMIUM", "AAD_PREMIUM_P2", "OFFICE_BUSINESS"] }, { "name": "standards.Branding", @@ -207,56 +148,29 @@ "helpText": "Sets the branding for the tenant. This includes the login page, and the Office 365 portal.", "executiveText": "Customizes Microsoft 365 login pages and portals with company branding, including logos, colors, and messaging. This creates a consistent corporate identity experience for employees and reinforces brand recognition while maintaining professional appearance across all Microsoft services.", "addedComponent": [ - { - "type": "textField", - "name": "standards.Branding.signInPageText", - "label": "Sign-in page text", - "required": false - }, - { - "type": "textField", - "name": "standards.Branding.usernameHintText", - "label": "Username hint Text", - "required": false - }, - { - "type": "switch", - "name": "standards.Branding.hideAccountResetCredentials", - "label": "Hide self-service password reset" - }, + { "type": "textField", "name": "standards.Branding.signInPageText", "label": "Sign-in page text", "required": false }, + { "type": "textField", "name": "standards.Branding.usernameHintText", "label": "Username hint Text", "required": false }, + { "type": "switch", "name": "standards.Branding.hideAccountResetCredentials", "label": "Hide self-service password reset" }, { "type": "autoComplete", "multiple": false, "label": "Visual Template", "name": "standards.Branding.layoutTemplateType", "options": [ - { - "label": "Full-screen background", - "value": "default" - }, - { - "label": "Partial-screen background", - "value": "verticalSplit" - } + { "label": "Full-screen background", "value": "default" }, + { "label": "Partial-screen background", "value": "verticalSplit" } ] }, - { - "type": "switch", - "name": "standards.Branding.isHeaderShown", - "label": "Show header" - }, - { - "type": "switch", - "name": "standards.Branding.isFooterShown", - "label": "Show footer" - } + { "type": "switch", "name": "standards.Branding.isHeaderShown", "label": "Show header" }, + { "type": "switch", "name": "standards.Branding.isFooterShown", "label": "Show footer" } ], "label": "Set branding for the tenant", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-05-13", "powershellEquivalent": "Portal only", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["AAD_PREMIUM", "AAD_PREMIUM_P2", "OFFICE_BUSINESS"] }, { "name": "standards.EnableCustomerLockbox", @@ -271,7 +185,8 @@ "impactColour": "info", "addedDate": "2024-01-08", "powershellEquivalent": "Set-OrganizationConfig -CustomerLockBoxEnabled $true", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["CustomerLockbox"] }, { "name": "standards.EnablePronouns", @@ -319,17 +234,7 @@ { "name": "standards.DisableGuestDirectory", "cat": "Global Standards", - "tag": [ - "CIS M365 5.0 (5.1.6.2)", - "CISA (MS.AAD.5.1v1)", - "EIDSCA.AP14", - "EIDSCA.ST08", - "EIDSCA.ST09", - "NIST CSF 2.0 (PR.AA-05)", - "EIDSCAAP07", - "EIDSCAST08", - "EIDSCAST09" - ], + "tag": ["CIS M365 5.0 (5.1.6.2)", "CISA (MS.AAD.5.1v1)", "EIDSCA.AP14", "EIDSCA.ST08", "EIDSCA.ST09", "NIST CSF 2.0 (PR.AA-05)", "EIDSCAAP07", "EIDSCAST08", "EIDSCAST09"], "helpText": "Disables Guest access to enumerate directory objects. This prevents guest users from seeing other users or guests in the directory.", "docsDescription": "Sets it so guests can view only their own user profile. Permission to view other users isn't allowed. Also restricts guest users from seeing the membership of groups they're in. See exactly what get locked down in the [Microsoft documentation.](https://learn.microsoft.com/en-us/entra/fundamentals/users-default-permissions)", "executiveText": "Restricts external guest users from viewing the company's employee directory and organizational structure, protecting sensitive information about staff and internal groups. This security measure prevents unauthorized access to corporate contact information while still allowing necessary collaboration.", @@ -354,19 +259,13 @@ "impactColour": "warning", "addedDate": "2021-11-16", "powershellEquivalent": "Set-TransportConfig -SmtpClientAuthenticationDisabled $true", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.ActivityBasedTimeout", "cat": "Global Standards", - "tag": [ - "CIS M365 5.0 (1.3.2)", - "spo_idle_session_timeout", - "NIST CSF 2.0 (PR.AA-03)", - "ZTNA21813", - "ZTNA21814", - "ZTNA21815" - ], + "tag": ["CIS M365 5.0 (1.3.2)", "spo_idle_session_timeout", "NIST CSF 2.0 (PR.AA-03)", "ZTNA21813", "ZTNA21814", "ZTNA21815"], "helpText": "Enables and sets Idle session timeout for Microsoft 365 to 1 hour. This policy affects most M365 web apps", "executiveText": "Automatically logs out inactive users from Microsoft 365 applications after a specified time period to prevent unauthorized access to company data on unattended devices. This security measure protects against data breaches when employees leave workstations unlocked.", "addedComponent": [ @@ -377,26 +276,11 @@ "label": "Select value", "name": "standards.ActivityBasedTimeout.timeout", "options": [ - { - "label": "1 Hour", - "value": "01:00:00" - }, - { - "label": "3 Hours", - "value": "03:00:00" - }, - { - "label": "6 Hours", - "value": "06:00:00" - }, - { - "label": "12 Hours", - "value": "12:00:00" - }, - { - "label": "24 Hours", - "value": "1.00:00:00" - } + { "label": "1 Hour", "value": "01:00:00" }, + { "label": "3 Hours", "value": "03:00:00" }, + { "label": "6 Hours", "value": "06:00:00" }, + { "label": "12 Hours", "value": "12:00:00" }, + { "label": "24 Hours", "value": "1.00:00:00" } ] } ], @@ -423,18 +307,9 @@ "name": "standards.AuthMethodsSettings.ReportSuspiciousActivity", "label": "Report Suspicious Activity Settings", "options": [ - { - "label": "Microsoft managed", - "value": "default" - }, - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Microsoft managed", "value": "default" }, + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, { @@ -445,18 +320,9 @@ "name": "standards.AuthMethodsSettings.SystemCredential", "label": "System Credential Preferences", "options": [ - { - "label": "Microsoft managed", - "value": "default" - }, - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Microsoft managed", "value": "default" }, + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] } ], @@ -497,14 +363,8 @@ "label": "App Approval Mode", "name": "standards.AppDeploy.mode", "options": [ - { - "label": "Template", - "value": "template" - }, - { - "label": "Copy Permissions", - "value": "copy" - } + { "label": "Template", "value": "template" }, + { "label": "Copy Permissions", "value": "copy" } ] }, { @@ -518,25 +378,15 @@ "labelField": "TemplateName", "valueField": "TemplateId", "queryKey": "StdAppApprovalTemplateList", - "addedField": { - "AppId": "AppId" - } + "addedField": { "AppId": "AppId" } }, - "condition": { - "field": "standards.AppDeploy.mode", - "compareType": "is", - "compareValue": "template" - } + "condition": { "field": "standards.AppDeploy.mode", "compareType": "is", "compareValue": "template" } }, { "type": "textField", "name": "standards.AppDeploy.appids", "label": "Application IDs, comma separated", - "condition": { - "field": "standards.AppDeploy.mode", - "compareType": "isNot", - "compareValue": "template" - } + "condition": { "field": "standards.AppDeploy.mode", "compareType": "isNot", "compareValue": "template" } } ], "label": "Deploy Application", @@ -564,23 +414,7 @@ { "name": "standards.PWdisplayAppInformationRequiredState", "cat": "Entra (AAD) Standards", - "tag": [ - "CIS M365 5.0 (2.3.1)", - "EIDSCA.AM03", - "EIDSCA.AM04", - "EIDSCA.AM06", - "EIDSCA.AM07", - "EIDSCA.AM09", - "EIDSCA.AM10", - "NIST CSF 2.0 (PR.AA-03)", - "EIDSCAAM01", - "EIDSCAAM03", - "EIDSCAAM04", - "EIDSCAAM06", - "EIDSCAAM07", - "EIDSCAAM09", - "EIDSCAAM10" - ], + "tag": ["CIS M365 5.0 (2.3.1)", "EIDSCA.AM03", "EIDSCA.AM04", "EIDSCA.AM06", "EIDSCA.AM07", "EIDSCA.AM09", "EIDSCA.AM10", "NIST CSF 2.0 (PR.AA-03)", "EIDSCAAM01", "EIDSCAAM03", "EIDSCAAM04", "EIDSCAAM06", "EIDSCAAM07", "EIDSCAAM09", "EIDSCAAM10"], "helpText": "Enables the MS authenticator app to display information about the app that is requesting authentication. This displays the application name.", "docsDescription": "Allows users to use Passwordless with Number Matching and adds location information from the last request", "executiveText": "Enhances authentication security by requiring users to match numbers and showing detailed information about login requests, including application names and location data. This helps employees verify legitimate login attempts and prevents unauthorized access through more secure authentication methods.", @@ -622,18 +456,9 @@ "label": "Select value", "name": "standards.PWcompanionAppAllowedState.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - }, - { - "label": "Microsoft managed", - "value": "default" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" }, + { "label": "Microsoft managed", "value": "default" } ] } ], @@ -647,21 +472,7 @@ { "name": "standards.EnableFIDO2", "cat": "Entra (AAD) Standards", - "tag": [ - "EIDSCA.AF01", - "EIDSCA.AF02", - "EIDSCA.AF03", - "EIDSCA.AF04", - "EIDSCA.AF05", - "EIDSCA.AF06", - "NIST CSF 2.0 (PR.AA-03)", - "EIDSCAAF01", - "EIDSCAAF02", - "EIDSCAAF03", - "EIDSCAAF04", - "EIDSCAAF05", - "EIDSCAAF06" - ], + "tag": ["EIDSCA.AF01", "EIDSCA.AF02", "EIDSCA.AF03", "EIDSCA.AF04", "EIDSCA.AF05", "EIDSCA.AF06", "NIST CSF 2.0 (PR.AA-03)", "EIDSCAAF01", "EIDSCAAF02", "EIDSCAAF03", "EIDSCAAF04", "EIDSCAAF05", "EIDSCAAF06"], "helpText": "Enables the FIDO2 authenticationMethod for the tenant", "docsDescription": "Enables FIDO2 capabilities for the tenant. This allows users to use FIDO2 keys like a Yubikey for authentication.", "executiveText": "Enables support for hardware security keys (like YubiKey) that provide the highest level of authentication security. These physical devices prevent phishing attacks and credential theft, offering superior protection for high-value accounts and sensitive business operations.", @@ -733,14 +544,8 @@ "label": "Select TAP Lifetime", "name": "standards.TAP.config", "options": [ - { - "label": "Only Once", - "value": "true" - }, - { - "label": "Multiple Logons", - "value": "false" - } + { "label": "Only Once", "value": "true" }, + { "label": "Multiple Logons", "value": "false" } ] } ], @@ -769,34 +574,19 @@ { "name": "standards.CustomBannedPasswordList", "cat": "Entra (AAD) Standards", - "tag": [ - "CIS M365 5.0 (5.2.3.2)", - "ZTNA21848", - "ZTNA21849", - "ZTNA21850", - "EIDSCAPR01", - "EIDSCAPR02", - "EIDSCAPR03", - "EIDSCAPR05", - "EIDSCAPR06" - ], + "tag": ["CIS M365 5.0 (5.2.3.2)", "ZTNA21848", "ZTNA21849", "ZTNA21850", "EIDSCAPR01", "EIDSCAPR02", "EIDSCAPR03", "EIDSCAPR05", "EIDSCAPR06"], "helpText": "**Requires Entra ID P1.** Updates and enables the Entra ID custom banned password list with the supplied words. Enter words separated by commas or semicolons. Each word must be 4-16 characters long. Maximum 1,000 words allowed.", "docsDescription": "Updates and enables the Entra ID custom banned password list with the supplied words. This supplements the global banned password list maintained by Microsoft. The custom list is limited to 1,000 key base terms of 4-16 characters each. Entra ID will [block variations and common substitutions](https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-configure-custom-password-protection#configure-custom-banned-passwords) of these words in user passwords. [How are passwords evaluated?](https://learn.microsoft.com/en-us/entra/identity/authentication/concept-password-ban-bad#score-calculation)", "addedComponent": [ - { - "type": "textField", - "name": "standards.CustomBannedPasswordList.BannedWords", - "label": "Banned Words", - "placeholder": "Banned words separated by commas or semicolons", - "required": true - } + { "type": "textField", "name": "standards.CustomBannedPasswordList.BannedWords", "label": "Banned Words", "placeholder": "Banned words separated by commas or semicolons", "required": true } ], "label": "Set Entra ID Custom Banned Password List", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2025-06-28", "powershellEquivalent": "Get-MgBetaDirectorySetting, New-MgBetaDirectorySetting, Update-MgBetaDirectorySetting", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["AAD_PREMIUM", "AAD_PREMIUM_P2"] }, { "name": "standards.ExternalMFATrusted", @@ -812,14 +602,8 @@ "label": "Select value", "name": "standards.ExternalMFATrusted.state", "options": [ - { - "label": "Enabled", - "value": "true" - }, - { - "label": "Disabled", - "value": "false" - } + { "label": "Enabled", "value": "true" }, + { "label": "Disabled", "value": "false" } ] } ], @@ -848,31 +632,12 @@ { "name": "standards.EnableAppConsentRequests", "cat": "Entra (AAD) Standards", - "tag": [ - "CIS M365 5.0 (1.5.2)", - "CISA (MS.AAD.9.1v1)", - "EIDSCA.CP04", - "EIDSCA.CR01", - "EIDSCA.CR02", - "EIDSCA.CR03", - "EIDSCA.CR04", - "Essential 8 (1507)", - "NIST CSF 2.0 (PR.AA-05)", - "ZTNA21869", - "EIDSCACR01", - "EIDSCACR02", - "EIDSCACR03", - "EIDSCACR04" - ], + "tag": ["CIS M365 5.0 (1.5.2)", "CISA (MS.AAD.9.1v1)", "EIDSCA.CP04", "EIDSCA.CR01", "EIDSCA.CR02", "EIDSCA.CR03", "EIDSCA.CR04", "Essential 8 (1507)", "NIST CSF 2.0 (PR.AA-05)", "ZTNA21869", "EIDSCACR01", "EIDSCACR02", "EIDSCACR03", "EIDSCACR04"], "helpText": "Enables App consent admin requests for the tenant via the GA role. Does not overwrite existing reviewer settings", "docsDescription": "Enables the ability for users to request admin consent for applications. Should be used in conjunction with the \"Require admin consent for applications\" standards", "executiveText": "Establishes a formal approval process where employees can request access to business applications that require administrative review. This balances security with productivity by allowing controlled access to necessary tools while preventing unauthorized application installations.", "addedComponent": [ - { - "type": "AdminRolesMultiSelect", - "label": "App Consent Reviewer Roles", - "name": "standards.EnableAppConsentRequests.ReviewerRoles" - } + { "type": "AdminRolesMultiSelect", "label": "App Consent Reviewer Roles", "name": "standards.EnableAppConsentRequests.ReviewerRoles" } ], "label": "Enable App consent admin requests", "impact": "Low Impact", @@ -896,14 +661,8 @@ "label": "Select value", "name": "standards.NudgeMFA.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, { @@ -937,19 +696,13 @@ "impactColour": "info", "addedDate": "2022-07-17", "powershellEquivalent": "Update-MgBetaDirectorySetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.DisableAppCreation", "cat": "Entra (AAD) Standards", - "tag": [ - "CIS M365 5.0 (1.2.2)", - "CISA (MS.AAD.4.1v1)", - "EIDSCA.AP10", - "Essential 8 (1175)", - "NIST CSF 2.0 (PR.AA-05)", - "EIDSCAAP10" - ], + "tag": ["CIS M365 5.0 (1.2.2)", "CISA (MS.AAD.4.1v1)", "EIDSCA.AP10", "Essential 8 (1175)", "NIST CSF 2.0 (PR.AA-05)", "EIDSCAAP10"], "helpText": "Disables the ability for users to create App registrations in the tenant.", "docsDescription": "Disables the ability for users to create applications in Entra. Done to prevent breached accounts from creating an app to maintain access to the tenant, even after the breached account has been secured.", "executiveText": "Prevents regular employees from creating application registrations that could be used to maintain unauthorized access to company systems. This security measure ensures that only authorized IT personnel can create applications, reducing the risk of persistent security breaches through malicious applications.", @@ -976,14 +729,8 @@ "label": "Select state", "name": "standards.BitLockerKeysForOwnedDevice.state", "options": [ - { - "label": "Restrict", - "value": "restrict" - }, - { - "label": "Allow", - "value": "allow" - } + { "label": "Restrict", "value": "restrict" }, + { "label": "Allow", "value": "allow" } ] } ], @@ -1029,17 +776,8 @@ "helpText": "**Requires 'Billing Administrator' GDAP role.** This standard disables all self service licenses and enables all exclusions", "executiveText": "Prevents employees from purchasing Microsoft 365 licenses independently, ensuring all software acquisitions go through proper procurement channels. This maintains budget control, prevents unauthorized spending, and ensures compliance with corporate licensing agreements.", "addedComponent": [ - { - "type": "textField", - "name": "standards.DisableSelfServiceLicenses.Exclusions", - "label": "License Ids to exclude from this standard", - "required": false - }, - { - "type": "switch", - "name": "standards.DisableSelfServiceLicenses.DisableTrials", - "label": "Disable starting trials on behalf of your organization" - } + { "type": "textField", "name": "standards.DisableSelfServiceLicenses.Exclusions", "label": "License Ids to exclude from this standard", "required": false }, + { "type": "switch", "name": "standards.DisableSelfServiceLicenses.DisableTrials", "label": "Disable starting trials on behalf of your organization" } ], "label": "Disable Self Service Licensing", "impact": "Medium Impact", @@ -1055,50 +793,25 @@ "helpText": "Blocks login for guest users that have not logged in for a number of days", "executiveText": "Automatically disables external guest accounts that haven't been used for a number of days, reducing security risks from dormant accounts while maintaining access for active external collaborators. This helps maintain a clean user directory and reduces potential attack vectors.", "addedComponent": [ - { - "type": "number", - "name": "standards.DisableGuests.days", - "required": true, - "defaultValue": 90, - "label": "Days of inactivity" - } + { "type": "number", "name": "standards.DisableGuests.days", "required": true, "defaultValue": 90, "label": "Days of inactivity" } ], "label": "Disable Guest accounts that have not logged on for a number of days", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2022-10-20", "powershellEquivalent": "Graph API", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["AAD_PREMIUM", "AAD_PREMIUM_P2"] }, { "name": "standards.OauthConsent", "cat": "Entra (AAD) Standards", - "tag": [ - "CIS M365 5.0 (1.5.1)", - "CISA (MS.AAD.4.2v1)", - "EIDSCA.AP08", - "EIDSCA.AP09", - "Essential 8 (1175)", - "NIST CSF 2.0 (PR.AA-05)", - "ZTNA21772", - "ZTNA21774", - "ZTNA21807", - "EIDSCAAP08", - "EIDSCAAP09", - "EIDSCACP01", - "EIDSCACP03", - "EIDSCACP04" - ], + "tag": ["CIS M365 5.0 (1.5.1)", "CISA (MS.AAD.4.2v1)", "EIDSCA.AP08", "EIDSCA.AP09", "Essential 8 (1175)", "NIST CSF 2.0 (PR.AA-05)", "ZTNA21772", "ZTNA21774", "ZTNA21807", "EIDSCAAP08", "EIDSCAAP09", "EIDSCACP01", "EIDSCACP03", "EIDSCACP04"], "helpText": "Disables users from being able to consent to applications, except for those specified in the field below", "docsDescription": "Requires users to get administrator consent before sharing data with applications. You can preapprove specific applications.", "executiveText": "Requires administrative approval before employees can grant applications access to company data, preventing unauthorized data sharing and potential security breaches. This protects against malicious applications while allowing approved business tools to function normally.", "addedComponent": [ - { - "type": "textField", - "name": "standards.OauthConsent.AllowedApps", - "label": "Allowed application IDs, comma separated", - "required": false - } + { "type": "textField", "name": "standards.OauthConsent.AllowedApps", "label": "Allowed application IDs, comma separated", "required": false } ], "label": "Require admin consent for applications (Prevent OAuth phishing)", "impact": "Medium Impact", @@ -1136,22 +849,10 @@ "label": "Who can send invites?", "name": "standards.GuestInvite.allowInvitesFrom", "options": [ - { - "label": "Everyone", - "value": "everyone" - }, - { - "label": "Admins, Guest inviters and All Members", - "value": "adminsGuestInvitersAndAllMembers" - }, - { - "label": "Admins and Guest inviters", - "value": "adminsAndGuestInviters" - }, - { - "label": "None", - "value": "none" - } + { "label": "Everyone", "value": "everyone" }, + { "label": "Admins, Guest inviters and All Members", "value": "adminsGuestInvitersAndAllMembers" }, + { "label": "Admins and Guest inviters", "value": "adminsAndGuestInviters" }, + { "label": "None", "value": "none" } ] } ], @@ -1179,17 +880,14 @@ } } ], - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": true - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": true }, "label": "Cleanup stale Entra devices", "impact": "High Impact", "impactColour": "danger", "addedDate": "2025-01-19", "powershellEquivalent": "Remove-MgDevice, Update-MgDevice or Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.UndoOauth", @@ -1297,20 +995,7 @@ { "name": "standards.PerUserMFA", "cat": "Entra (AAD) Standards", - "tag": [ - "CIS M365 5.0 (1.2.1)", - "CIS M365 5.0 (1.1.1)", - "CIS M365 5.0 (1.1.2)", - "CISA (MS.AAD.1.1v1)", - "CISA (MS.AAD.1.2v1)", - "Essential 8 (1504)", - "Essential 8 (1173)", - "Essential 8 (1401)", - "NIST CSF 2.0 (PR.AA-03)", - "ZTNA21780", - "ZTNA21782", - "ZTNA21796" - ], + "tag": ["CIS M365 5.0 (1.2.1)", "CIS M365 5.0 (1.1.1)", "CIS M365 5.0 (1.1.2)", "CISA (MS.AAD.1.1v1)", "CISA (MS.AAD.1.2v1)", "Essential 8 (1504)", "Essential 8 (1173)", "Essential 8 (1401)", "NIST CSF 2.0 (PR.AA-03)", "ZTNA21780", "ZTNA21782", "ZTNA21796"], "helpText": "Enables per user MFA for all users.", "executiveText": "Requires all employees to use multi-factor authentication for enhanced account security, significantly reducing the risk of unauthorized access from compromised passwords. This fundamental security measure protects against the majority of account-based attacks and is essential for maintaining strong cybersecurity posture.", "addedComponent": [], @@ -1334,11 +1019,7 @@ "creatable": false, "name": "standards.UserPreferredLanguage.preferredLanguage", "label": "Preferred Language", - "api": { - "url": "/languageList.json", - "labelField": "tag", - "valueField": "tag" - } + "api": { "url": "/languageList.json", "labelField": "tag", "valueField": "tag" } } ], "label": "Preferred language for all users", @@ -1364,14 +1045,8 @@ "name": "standards.AppManagementPolicy.passwordCredentialsPasswordAddition", "label": "Disable Password Addition", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, { @@ -1382,28 +1057,12 @@ "name": "standards.AppManagementPolicy.passwordCredentialsCustomPasswordAddition", "label": "Disable Custom Password", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, - { - "type": "number", - "required": false, - "name": "standards.AppManagementPolicy.passwordCredentialsMaxLifetime", - "label": "Password Credentials Max Lifetime (Days)" - }, - { - "type": "number", - "required": false, - "name": "standards.AppManagementPolicy.keyCredentialsMaxLifetime", - "label": "Key Credentials Max Lifetime (Days)" - } + { "type": "number", "required": false, "name": "standards.AppManagementPolicy.passwordCredentialsMaxLifetime", "label": "Password Credentials Max Lifetime (Days)" }, + { "type": "number", "required": false, "name": "standards.AppManagementPolicy.keyCredentialsMaxLifetime", "label": "Key Credentials Max Lifetime (Days)" } ], "label": "Set Default App Management Policy", "impact": "Medium Impact", @@ -1419,18 +1078,15 @@ "helpText": "Set the Outbound Spam Alert e-mail address", "docsDescription": "Sets the e-mail address to which outbound spam alerts are sent.", "addedComponent": [ - { - "type": "textField", - "name": "standards.OutBoundSpamAlert.OutboundSpamContact", - "label": "Outbound spam contact" - } + { "type": "textField", "name": "standards.OutBoundSpamAlert.OutboundSpamContact", "label": "Outbound spam contact" } ], "label": "Set Outbound Spam Alert e-mail", "impact": "Low Impact", "impactColour": "info", "addedDate": "2023-05-03", "powershellEquivalent": "Set-HostedOutboundSpamFilterPolicy", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.MessageExpiration", @@ -1444,7 +1100,8 @@ "impactColour": "info", "addedDate": "2024-02-23", "powershellEquivalent": "Set-TransportConfig -MessageExpirationTimeout 12.00:00:00", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.GlobalQuarantineNotifications", @@ -1459,18 +1116,9 @@ "label": "Select value", "name": "standards.GlobalQuarantineNotifications.NotificationInterval", "options": [ - { - "label": "4 hours", - "value": "04:00:00" - }, - { - "label": "1 day/Daily", - "value": "1.00:00:00" - }, - { - "label": "7 days/Weekly", - "value": "7.00:00:00" - } + { "label": "4 hours", "value": "04:00:00" }, + { "label": "1 day/Daily", "value": "1.00:00:00" }, + { "label": "7 days/Weekly", "value": "7.00:00:00" } ] } ], @@ -1479,7 +1127,8 @@ "impactColour": "info", "addedDate": "2024-05-03", "powershellEquivalent": "Set-QuarantinePolicy -EndUserSpamNotificationFrequency", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DisableTNEF", @@ -1494,7 +1143,8 @@ "impactColour": "info", "addedDate": "2024-04-26", "powershellEquivalent": "Set-RemoteDomain -Identity 'Default' -TNEFEnabled $false", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.FocusedInbox", @@ -1510,14 +1160,8 @@ "label": "Select value", "name": "standards.FocusedInbox.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] } ], @@ -1526,7 +1170,8 @@ "impactColour": "info", "addedDate": "2024-04-26", "powershellEquivalent": "Set-OrganizationConfig -FocusedInboxOn $true or $false", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.CloudMessageRecall", @@ -1542,14 +1187,8 @@ "label": "Select value", "name": "standards.CloudMessageRecall.state", "options": [ - { - "label": "Enabled", - "value": "true" - }, - { - "label": "Disabled", - "value": "false" - } + { "label": "Enabled", "value": "true" }, + { "label": "Disabled", "value": "false" } ] } ], @@ -1558,7 +1197,8 @@ "impactColour": "info", "addedDate": "2024-05-31", "powershellEquivalent": "Set-OrganizationConfig -MessageRecallEnabled", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AutoExpandArchive", @@ -1573,7 +1213,8 @@ "impactColour": "info", "addedDate": "2021-11-16", "powershellEquivalent": "Set-OrganizationConfig -AutoExpandingArchive", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.TwoClickEmailProtection", @@ -1590,14 +1231,8 @@ "label": "Select value", "name": "standards.TwoClickEmailProtection.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] } ], @@ -1606,7 +1241,8 @@ "impactColour": "info", "addedDate": "2025-06-13", "powershellEquivalent": "Set-OrganizationConfig -TwoClickMailPreviewEnabled $true | $false", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EnableOnlineArchiving", @@ -1620,7 +1256,8 @@ "impactColour": "info", "addedDate": "2024-01-20", "powershellEquivalent": "Enable-Mailbox -Archive $true", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EnableLitigationHold", @@ -1629,20 +1266,15 @@ "helpText": "Enables litigation hold for all UserMailboxes with a valid license.", "executiveText": "Preserves all email content for legal and compliance purposes by preventing permanent deletion of emails, even when users attempt to delete them. This is essential for organizations subject to legal discovery requirements or regulatory compliance mandates.", "addedComponent": [ - { - "type": "textField", - "name": "standards.EnableLitigationHold.days", - "required": false, - "label": "Days to apply for litigation hold", - "helperText": "Number of days to apply litigation hold for. If left blank or set to Unlimited, litigation hold will be applied indefinitely." - } + { "type": "textField", "name": "standards.EnableLitigationHold.days", "required": false, "label": "Days to apply for litigation hold", "helperText": "Number of days to apply litigation hold for. If left blank or set to Unlimited, litigation hold will be applied indefinitely." } ], "label": "Enable Litigation Hold for all users", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-06-25", "powershellEquivalent": "Set-Mailbox -LitigationHoldEnabled $true", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SpoofWarn", @@ -1658,31 +1290,19 @@ "label": "Select value", "name": "standards.SpoofWarn.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "label": "Enter allowed senders(domain.com, *.domain.com or test@domain.com)", - "name": "standards.SpoofWarn.AllowListAdd" - } + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "label": "Enter allowed senders(domain.com, *.domain.com or test@domain.com)", "name": "standards.SpoofWarn.AllowListAdd" } ], "label": "Enable or disable 'external' warning in Outlook", "impact": "Low Impact", "impactColour": "info", "addedDate": "2021-11-16", - "powershellEquivalent": "Set-ExternalInOutlook \u2013Enabled $true or $false", - "recommendedBy": ["CIS", "CIPP"] + "powershellEquivalent": "Set-ExternalInOutlook –Enabled $true or $false", + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EnableMailTips", @@ -1691,20 +1311,15 @@ "helpText": "Enables all MailTips in Outlook. MailTips are the notifications Outlook and Outlook on the web shows when an email you create, meets some requirements", "executiveText": "Enables helpful notifications in Outlook that warn users about potential email issues, such as sending to large groups, external recipients, or invalid addresses. This reduces email mistakes and improves communication efficiency by providing real-time guidance to employees.", "addedComponent": [ - { - "type": "number", - "name": "standards.EnableMailTips.MailTipsLargeAudienceThreshold", - "label": "Number of recipients to trigger the large audience MailTip (Default is 25)", - "placeholder": "Enter a profile name", - "defaultValue": 25 - } + { "type": "number", "name": "standards.EnableMailTips.MailTipsLargeAudienceThreshold", "label": "Number of recipients to trigger the large audience MailTip (Default is 25)", "placeholder": "Enter a profile name", "defaultValue": 25 } ], "label": "Enable all MailTips", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-01-14", "powershellEquivalent": "Set-OrganizationConfig", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.TeamsMeetingsByDefault", @@ -1720,14 +1335,8 @@ "label": "Select value", "name": "standards.TeamsMeetingsByDefault.state", "options": [ - { - "label": "Enabled", - "value": "true" - }, - { - "label": "Disabled", - "value": "false" - } + { "label": "Enabled", "value": "true" }, + { "label": "Disabled", "value": "false" } ] } ], @@ -1736,7 +1345,8 @@ "impactColour": "info", "addedDate": "2024-05-31", "powershellEquivalent": "Set-OrganizationConfig -OnlineMeetingsByDefaultEnabled", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DisableViva", @@ -1765,7 +1375,8 @@ "impactColour": "info", "addedDate": "2023-03-14", "powershellEquivalent": "Rotate-DkimSigningConfig", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EnableExchangeCloudManagement", @@ -1781,14 +1392,8 @@ "name": "standards.EnableExchangeCloudManagement.state", "label": "Cloud Management State", "options": [ - { - "label": "Cloud Management", - "value": true - }, - { - "label": "On-Premises Management", - "value": false - } + { "label": "Cloud Management", "value": true }, + { "label": "On-Premises Management", "value": false } ] } ], @@ -1797,7 +1402,8 @@ "impactColour": "info", "addedDate": "2026-03-28", "powershellEquivalent": "Set-Mailbox -Identity user@domain.com -IsExchangeCloudManaged $true or $false", - "recommendedBy": ["Microsoft", "CIPP"] + "recommendedBy": ["Microsoft", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV"] }, { "name": "standards.AddDKIM", @@ -1811,7 +1417,8 @@ "impactColour": "info", "addedDate": "2023-03-14", "powershellEquivalent": "New-DkimSigningConfig and Set-DkimSigningConfig", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AddDMARCToMOERA", @@ -1830,10 +1437,7 @@ "label": "Value", "name": "standards.AddDMARCToMOERA.RecordValue", "options": [ - { - "label": "v=DMARC1; p=reject; (recommended)", - "value": "v=DMARC1; p=reject;" - } + { "label": "v=DMARC1; p=reject; (recommended)", "value": "v=DMARC1; p=reject;" } ] } ], @@ -1843,23 +1447,12 @@ "addedDate": "2025-06-16", "powershellEquivalent": "Portal only", "recommendedBy": ["CIS", "Microsoft"], - "disabledFeatures": { - "remediate": true - } + "disabledFeatures": { "remediate": true } }, { "name": "standards.EnableMailboxAuditing", "cat": "Exchange Standards", - "tag": [ - "CIS M365 5.0 (6.1.1)", - "CIS M365 5.0 (6.1.2)", - "CIS M365 5.0 (6.1.3)", - "exo_mailboxaudit", - "Essential 8 (1509)", - "Essential 8 (1683)", - "NIST CSF 2.0 (DE.CM-09)", - "CISAMSEXO131" - ], + "tag": ["CIS M365 5.0 (6.1.1)", "CIS M365 5.0 (6.1.2)", "CIS M365 5.0 (6.1.3)", "exo_mailboxaudit", "Essential 8 (1509)", "Essential 8 (1683)", "NIST CSF 2.0 (DE.CM-09)", "CISAMSEXO131"], "helpText": "Enables Mailbox auditing for all mailboxes and on tenant level. Disables audit bypass on all mailboxes. Unified Audit Log needs to be enabled for this standard to function.", "docsDescription": "Enables mailbox auditing on tenant level and for all mailboxes. Disables audit bypass on all mailboxes. By default Microsoft does not enable mailbox auditing for Resource Mailboxes, Public Folder Mailboxes and DiscoverySearch Mailboxes. Unified Audit Log needs to be enabled for this standard to function.", "executiveText": "Enables comprehensive logging of all email access and modifications across all employee mailboxes, providing detailed audit trails for security investigations and compliance requirements. This helps detect unauthorized access, data breaches, and supports regulatory compliance efforts.", @@ -1869,7 +1462,8 @@ "impactColour": "info", "addedDate": "2024-01-08", "powershellEquivalent": "Set-OrganizationConfig -AuditDisabled $false", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AutoArchive", @@ -1895,7 +1489,8 @@ "impactColour": "info", "addedDate": "2025-12-11", "powershellEquivalent": "Set-OrganizationConfig -AutoArchivingThresholdPercentage 80-100", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AutoArchiveMailbox", @@ -1912,14 +1507,8 @@ "label": "Select value", "name": "standards.AutoArchiveMailbox.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] } ], @@ -1928,7 +1517,8 @@ "impactColour": "info", "addedDate": "2026-01-16", "powershellEquivalent": "Set-OrganizationConfig -AutoEnableArchiveMailbox $true|$false", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SendReceiveLimitTenant", @@ -1963,7 +1553,8 @@ "impactColour": "info", "addedDate": "2023-11-16", "powershellEquivalent": "Set-MailboxPlan", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.calDefault", @@ -1972,11 +1563,7 @@ "helpText": "Sets the default sharing level for the default calendar, for all users", "docsDescription": "Sets the default sharing level for the default calendar for all users in the tenant. You can read about the different sharing levels [here.](https://learn.microsoft.com/en-us/powershell/module/exchange/set-mailboxfolderpermission?view=exchange-ps#-accessrights)", "executiveText": "Configures how much calendar information employees share by default with colleagues, balancing collaboration needs with privacy. This setting determines whether others can see meeting details, free/busy times, or just availability, helping optimize scheduling while protecting sensitive meeting information.", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "addedComponent": [ { "type": "autoComplete", @@ -1984,50 +1571,17 @@ "label": "Select Sharing Level", "name": "standards.calDefault.permissionLevel", "options": [ - { - "label": "Owner - The user can create, read, edit, and delete all items in the folder, and create subfolders. The user is both folder owner and folder contact.", - "value": "Owner" - }, - { - "label": "Publishing Editor - The user can create, read, edit, and delete all items in the folder, and create subfolders.", - "value": "PublishingEditor" - }, - { - "label": "Editor - The user can create items in the folder. The contents of the folder do not appear.", - "value": "Editor" - }, - { - "label": "Publishing Author. The user can read, create all items/subfolders. Can modify and delete only items they create.", - "value": "PublishingAuthor" - }, - { - "label": "Author - The user can create and read items, and modify and delete items that they create.", - "value": "Author" - }, - { - "label": "Non Editing Author - The user has full read access and create items. Can can delete only own items.", - "value": "NonEditingAuthor" - }, - { - "label": "Reviewer - The user can read all items in the folder.", - "value": "Reviewer" - }, - { - "label": "Contributor - The user can create items and folders.", - "value": "Contributor" - }, - { - "label": "Availability Only - Indicates that the user can view only free/busy time within the calendar.", - "value": "AvailabilityOnly" - }, - { - "label": "Limited Details - The user can view free/busy time within the calendar and the subject and location of appointments.", - "value": "LimitedDetails" - }, - { - "label": "None - The user has no permissions on the folder.", - "value": "none" - } + { "label": "Owner - The user can create, read, edit, and delete all items in the folder, and create subfolders. The user is both folder owner and folder contact.", "value": "Owner" }, + { "label": "Publishing Editor - The user can create, read, edit, and delete all items in the folder, and create subfolders.", "value": "PublishingEditor" }, + { "label": "Editor - The user can create items in the folder. The contents of the folder do not appear.", "value": "Editor" }, + { "label": "Publishing Author. The user can read, create all items/subfolders. Can modify and delete only items they create.", "value": "PublishingAuthor" }, + { "label": "Author - The user can create and read items, and modify and delete items that they create.", "value": "Author" }, + { "label": "Non Editing Author - The user has full read access and create items. Can can delete only own items.", "value": "NonEditingAuthor" }, + { "label": "Reviewer - The user can read all items in the folder.", "value": "Reviewer" }, + { "label": "Contributor - The user can create items and folders.", "value": "Contributor" }, + { "label": "Availability Only - Indicates that the user can view only free/busy time within the calendar.", "value": "AvailabilityOnly" }, + { "label": "Limited Details - The user can view free/busy time within the calendar and the subject and location of appointments.", "value": "LimitedDetails" }, + { "label": "None - The user has no permissions on the folder.", "value": "none" } ] } ], @@ -2036,7 +1590,8 @@ "impactColour": "info", "addedDate": "2023-04-27", "powershellEquivalent": "Set-MailboxFolderPermission", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EXOOutboundSpamLimits", @@ -2083,18 +1638,9 @@ "name": "standards.EXOOutboundSpamLimits.ActionWhenThresholdReached", "label": "Action When Threshold Reached", "options": [ - { - "label": "Alert", - "value": "Alert" - }, - { - "label": "Block User", - "value": "BlockUser" - }, - { - "label": "Block user from sending mail for the rest of the day", - "value": "BlockUserForToday" - } + { "label": "Alert", "value": "Alert" }, + { "label": "Block User", "value": "BlockUser" }, + { "label": "Block user from sending mail for the rest of the day", "value": "BlockUserForToday" } ] } ], @@ -2103,7 +1649,8 @@ "impactColour": "info", "addedDate": "2025-05-13", "powershellEquivalent": "Set-HostedOutboundSpamFilterPolicy", - "recommendedBy": ["CIPP", "CIS"] + "recommendedBy": ["CIPP", "CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DisableExternalCalendarSharing", @@ -2118,7 +1665,8 @@ "impactColour": "info", "addedDate": "2024-01-08", "powershellEquivalent": "Get-SharingPolicy | Set-SharingPolicy -Enabled $False", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AutoAddProxy", @@ -2134,11 +1682,7 @@ "addedDate": "2025-02-07", "powershellEquivalent": "Set-Mailbox -EmailAddresses @{add=$EmailAddress}", "recommendedBy": [], - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - } + "disabledFeatures": { "report": true, "warn": true, "remediate": false } }, { "name": "standards.DisableAdditionalStorageProviders", @@ -2153,7 +1697,8 @@ "impactColour": "info", "addedDate": "2024-01-17", "powershellEquivalent": "Get-OwaMailboxPolicy | Set-OwaMailboxPolicy -AdditionalStorageProvidersEnabled $False", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AntiSpamSafeList", @@ -2163,18 +1708,15 @@ "docsDescription": "Sets [Microsoft's built-in 'safe list'](https://learn.microsoft.com/en-us/powershell/module/exchange/set-hostedconnectionfilterpolicy?view=exchange-ps#-enablesafelist) in the anti-spam connection filter policy, rather than setting a custom safe/block list of IPs.", "executiveText": "Enables Microsoft's pre-approved list of trusted email servers to improve email delivery from legitimate sources while maintaining spam protection. This reduces false positives where legitimate emails might be blocked while still protecting against spam and malicious emails.", "addedComponent": [ - { - "type": "switch", - "name": "standards.AntiSpamSafeList.EnableSafeList", - "label": "Enable Safe List" - } + { "type": "switch", "name": "standards.AntiSpamSafeList.EnableSafeList", "label": "Enable Safe List" } ], "label": "Set Anti-Spam Connection Filter Safe List", "impact": "Medium Impact", "impactColour": "info", "addedDate": "2025-02-15", "powershellEquivalent": "Set-HostedConnectionFilterPolicy \"Default\" -EnableSafeList $true", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.ShortenMeetings", @@ -2189,18 +1731,9 @@ "label": "Select value", "name": "standards.ShortenMeetings.ShortenEventScopeDefault", "options": [ - { - "label": "Disabled/None", - "value": "None" - }, - { - "label": "End early", - "value": "EndEarly" - }, - { - "label": "Start late", - "value": "StartLate" - } + { "label": "Disabled/None", "value": "None" }, + { "label": "End early", "value": "EndEarly" }, + { "label": "Start late", "value": "StartLate" } ] }, { @@ -2229,7 +1762,8 @@ "impactColour": "warning", "addedDate": "2024-05-27", "powershellEquivalent": "Set-OrganizationConfig -ShortenEventScopeDefault -DefaultMinutesToReduceShortEventsBy -DefaultMinutesToReduceLongEventsBy", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.Bookings", @@ -2245,14 +1779,8 @@ "label": "Select value", "name": "standards.Bookings.state", "options": [ - { - "label": "Enabled", - "value": "true" - }, - { - "label": "Disabled", - "value": "false" - } + { "label": "Enabled", "value": "true" }, + { "label": "Disabled", "value": "false" } ] } ], @@ -2261,7 +1789,8 @@ "impactColour": "warning", "addedDate": "2024-05-31", "powershellEquivalent": "Set-OrganizationConfig -BookingsEnabled", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EXODirectSend", @@ -2278,14 +1807,8 @@ "label": "Select value", "name": "standards.EXODirectSend.state", "options": [ - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] } ], @@ -2299,13 +1822,7 @@ { "name": "standards.DisableOutlookAddins", "cat": "Exchange Standards", - "tag": [ - "CIS M365 5.0 (6.3.1)", - "exo_outlookaddins", - "NIST CSF 2.0 (PR.AA-05)", - "NIST CSF 2.0 (PR.PS-05)", - "ZTNA21817" - ], + "tag": ["CIS M365 5.0 (6.3.1)", "exo_outlookaddins", "NIST CSF 2.0 (PR.AA-05)", "NIST CSF 2.0 (PR.PS-05)", "ZTNA21817"], "helpText": "Disables the ability for users to install add-ins in Outlook. This is to prevent users from installing malicious add-ins.", "docsDescription": "Disables users from being able to install add-ins in Outlook. Only admins are able to approve add-ins for the users. This is done to reduce the threat surface for data exfiltration.", "executiveText": "Prevents employees from installing third-party add-ins in Outlook without administrative approval, reducing security risks from potentially malicious extensions. This ensures only vetted and approved tools can access company email data while maintaining centralized control over email functionality.", @@ -2315,7 +1832,8 @@ "impactColour": "warning", "addedDate": "2024-02-05", "powershellEquivalent": "Get-ManagementRoleAssignment | Remove-ManagementRoleAssignment", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SafeSendersDisable", @@ -2324,17 +1842,14 @@ "helpText": "Loops through all users and removes the Safe Senders list. This is to prevent SPF bypass attacks, as the Safe Senders list is not checked by SPF.", "executiveText": "Removes user-defined safe sender lists to prevent security bypasses where malicious emails could avoid spam filtering. This ensures all emails go through proper security screening, even if users have previously marked senders as 'safe', improving overall email security.", "addedComponent": [], - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "label": "Remove Safe Senders to prevent SPF bypass", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2023-10-26", "powershellEquivalent": "Set-MailboxJunkEmailConfiguration", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DelegateSentItems", @@ -2344,18 +1859,15 @@ "docsDescription": "This makes sure that e-mails sent from shared mailboxes or delegate mailboxes, end up in the mailbox of the shared/delegate mailbox instead of the sender, allowing you to keep replies in the same mailbox as the original e-mail.", "executiveText": "Ensures emails sent from shared mailboxes (like info@company.com) are stored in the shared mailbox rather than the individual sender's mailbox. This maintains complete email threads in one location, improving collaboration and ensuring all team members can see the full conversation history.", "addedComponent": [ - { - "type": "switch", - "label": "Include user mailboxes", - "name": "standards.DelegateSentItems.IncludeUserMailboxes" - } + { "type": "switch", "label": "Include user mailboxes", "name": "standards.DelegateSentItems.IncludeUserMailboxes" } ], "label": "Set mailbox Sent Items delegation (Sent items for shared mailboxes)", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2021-11-16", "powershellEquivalent": "Set-Mailbox", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SendFromAlias", @@ -2382,7 +1894,8 @@ "impactColour": "warning", "addedDate": "2022-05-25", "powershellEquivalent": "Set-Mailbox", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.UserSubmissions", @@ -2398,29 +1911,19 @@ "label": "Select value", "name": "standards.UserSubmissions.state", "options": [ - { - "label": "Enabled", - "value": "enable" - }, - { - "label": "Disabled", - "value": "disable" - } + { "label": "Enabled", "value": "enable" }, + { "label": "Disabled", "value": "disable" } ] }, - { - "type": "textField", - "name": "standards.UserSubmissions.email", - "required": false, - "label": "Destination email address" - } + { "type": "textField", "name": "standards.UserSubmissions.email", "required": false, "label": "Destination email address" } ], "label": "Set the state of the built-in Report button in Outlook", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2024-06-28", "powershellEquivalent": "New-ReportSubmissionPolicy or Set-ReportSubmissionPolicy and New-ReportSubmissionRule or Set-ReportSubmissionRule", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DisableSharedMailbox", @@ -2450,18 +1953,13 @@ "impactColour": "warning", "addedDate": "2025-06-01", "powershellEquivalent": "Get-Mailbox & Update-MgUser", - "recommendedBy": ["Microsoft", "CIPP"] + "recommendedBy": ["Microsoft", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.EXODisableAutoForwarding", "cat": "Exchange Standards", - "tag": [ - "CIS M365 5.0 (6.2.1)", - "mdo_autoforwardingmode", - "mdo_blockmailforward", - "CISA (MS.EXO.4.1v1)", - "NIST CSF 2.0 (PR.DS-02)" - ], + "tag": ["CIS M365 5.0 (6.2.1)", "mdo_autoforwardingmode", "mdo_blockmailforward", "CISA (MS.EXO.4.1v1)", "NIST CSF 2.0 (PR.DS-02)"], "helpText": "Disables the ability for users to automatically forward e-mails to external recipients.", "docsDescription": "Disables the ability for users to automatically forward e-mails to external recipients. This is to prevent data exfiltration. Please check if there are any legitimate use cases for this feature before implementing, like forwarding invoices and such.", "executiveText": "Prevents employees from automatically forwarding company emails to external addresses, protecting against data leaks and unauthorized information sharing. This security measure helps maintain control over sensitive business communications while preventing both accidental and intentional data exfiltration.", @@ -2471,7 +1969,8 @@ "impactColour": "danger", "addedDate": "2024-07-26", "powershellEquivalent": "Set-HostedOutboundSpamFilterPolicy -AutoForwardingMode 'Off'", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.RetentionPolicyTag", @@ -2481,19 +1980,15 @@ "docsDescription": "Creates a CIPP - Deleted Items retention policy tag that permanently deletes items in the Deleted Items folder after X days.", "executiveText": "Automatically and permanently removes deleted emails after a specified number of days, helping manage storage costs and ensuring compliance with data retention policies. This prevents accumulation of unnecessary deleted items while maintaining a reasonable recovery window for accidentally deleted emails.", "addedComponent": [ - { - "type": "number", - "name": "standards.RetentionPolicyTag.AgeLimitForRetention", - "label": "Retention Days", - "required": true - } + { "type": "number", "name": "standards.RetentionPolicyTag.AgeLimitForRetention", "label": "Retention Days", "required": true } ], "label": "Retention Policy, permanently delete items in Deleted Items after X days", "impact": "High Impact", "impactColour": "danger", "addedDate": "2025-02-02", "powershellEquivalent": "Set-RetentionPolicyTag", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.QuarantineRequestAlert", @@ -2503,18 +1998,15 @@ "docsDescription": "Sets a e-mail address to alert when a User requests to release a quarantined message. This is useful for monitoring and ensuring that the correct messages are released.", "executiveText": "Notifies IT administrators when employees request to release emails that were quarantined for security reasons, enabling oversight of potentially dangerous messages. This helps ensure that legitimate emails are released while maintaining security controls over suspicious content.", "addedComponent": [ - { - "type": "textField", - "name": "standards.QuarantineRequestAlert.NotifyUser", - "label": "E-mail to receive the alert" - } + { "type": "textField", "name": "standards.QuarantineRequestAlert.NotifyUser", "label": "E-mail to receive the alert" } ], "label": "Quarantine Release Request Alert", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-07-15", "powershellEquivalent": "New-ProtectionAlert and Set-ProtectionAlert", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SharePointMassDeletionAlert", @@ -2524,44 +2016,24 @@ "docsDescription": "Sets a e-mail address to alert when a User deletes more than 20 SharePoint files within 60 minutes. This is useful for monitoring and ensuring that the correct SharePoint files are deleted. NB: Requires a Office 365 E5 subscription, Office 365 E3 with Threat Intelligence or Office 365 EquivioAnalytics add-on.", "executiveText": "Alerts administrators when employees delete large numbers of SharePoint files in a short time period, helping detect potential data destruction attacks, ransomware, or accidental mass deletions. This early warning system enables rapid response to protect critical business documents and data.", "addedComponent": [ - { - "type": "number", - "name": "standards.SharePointMassDeletionAlert.Threshold", - "label": "Max files to delete within the time frame", - "defaultValue": 20 - }, - { - "type": "number", - "name": "standards.SharePointMassDeletionAlert.TimeWindow", - "label": "Time frame in minutes", - "defaultValue": 60 - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": true, - "name": "standards.SharePointMassDeletionAlert.NotifyUser", - "label": "E-mail to receive the alert" - } + { "type": "number", "name": "standards.SharePointMassDeletionAlert.Threshold", "label": "Max files to delete within the time frame", "defaultValue": 20 }, + { "type": "number", "name": "standards.SharePointMassDeletionAlert.TimeWindow", "label": "Time frame in minutes", "defaultValue": 60 }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": true, "name": "standards.SharePointMassDeletionAlert.NotifyUser", "label": "E-mail to receive the alert" } ], "label": "SharePoint Mass Deletion Alert", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-04-07", "powershellEquivalent": "New-ProtectionAlert and Set-ProtectionAlert", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["RMS_S_PREMIUM2"] }, { "name": "standards.SafeLinksTemplatePolicy", "label": "SafeLinks Policy Template", "cat": "Templates", "multiple": false, - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "impact": "Medium Impact", "addedDate": "2025-04-29", "helpText": "Deploy and manage SafeLinks policy templates to protect against malicious URLs in emails and Office documents.", @@ -2573,127 +2045,38 @@ "creatable": false, "name": "standards.SafeLinksTemplatePolicy.TemplateIds", "label": "Select SafeLinks Policy Templates", - "api": { - "url": "/api/ListSafeLinksPolicyTemplates", - "labelField": "TemplateName", - "valueField": "GUID", - "queryKey": "ListSafeLinksPolicyTemplates" - } + "api": { "url": "/api/ListSafeLinksPolicyTemplates", "labelField": "TemplateName", "valueField": "GUID", "queryKey": "ListSafeLinksPolicyTemplates" } } - ] + ], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SafeLinksPolicy", "cat": "Defender Standards", - "tag": [ - "CIS M365 5.0 (2.1.1)", - "mdo_safelinksforemail", - "mdo_safelinksforOfficeApps", - "NIST CSF 2.0 (DE.CM-09)", - "ORCA105", - "ORCA106", - "ORCA107", - "ORCA112", - "ORCA113", - "ORCA114", - "ORCA116", - "ORCA119", - "ORCA156", - "ORCA179", - "ORCA226", - "ORCA236", - "ORCA237", - "ORCA238", - "CISAMSEXO151", - "CISAMSEXO152", - "CISAMSEXO153" - ], + "tag": ["CIS M365 5.0 (2.1.1)", "mdo_safelinksforemail", "mdo_safelinksforOfficeApps", "NIST CSF 2.0 (DE.CM-09)", "ORCA105", "ORCA106", "ORCA107", "ORCA112", "ORCA113", "ORCA114", "ORCA116", "ORCA119", "ORCA156", "ORCA179", "ORCA226", "ORCA236", "ORCA237", "ORCA238", "CISAMSEXO151", "CISAMSEXO152", "CISAMSEXO153"], "helpText": "This creates a Safe Links policy that automatically scans, tracks, and and enables safe links for Email, Office, and Teams for both external and internal senders", "addedComponent": [ - { - "type": "textField", - "name": "standards.SafeLinksPolicy.name", - "label": "Policy Name", - "required": true, - "defaultValue": "CIPP Default SafeLinks Policy" - }, - { - "type": "switch", - "label": "AllowClickThrough", - "name": "standards.SafeLinksPolicy.AllowClickThrough" - }, - { - "type": "switch", - "label": "DisableUrlRewrite", - "name": "standards.SafeLinksPolicy.DisableUrlRewrite" - }, - { - "type": "switch", - "label": "EnableOrganizationBranding", - "name": "standards.SafeLinksPolicy.EnableOrganizationBranding" - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "name": "standards.SafeLinksPolicy.DoNotRewriteUrls", - "label": "Do not rewrite the following URLs in email" - } - ], - "label": "Default Safe Links Policy", - "impact": "Low Impact", - "impactColour": "info", - "addedDate": "2024-03-25", - "powershellEquivalent": "Set-SafeLinksPolicy or New-SafeLinksPolicy", - "recommendedBy": ["CIS"] - }, - { - "name": "standards.AntiPhishPolicy", - "cat": "Defender Standards", - "tag": [ - "mdo_safeattachments", - "mdo_highconfidencespamaction", - "mdo_highconfidencephishaction", - "mdo_phisspamacation", - "mdo_spam_notifications_only_for_admins", - "mdo_antiphishingpolicies", - "mdo_phishthresholdlevel", - "CIS M365 5.0 (2.1.7)", - "NIST CSF 2.0 (DE.CM-09)", - "ORCA104", - "ORCA115", - "ORCA180", - "ORCA220", - "ORCA221", - "ORCA222", - "ORCA223", - "ORCA228", - "ORCA229", - "ORCA230", - "ORCA233", - "ORCA234", - "ORCA235", - "ORCA239", - "ORCA242", - "ORCA243", - "ORCA244", - "ZTNA21784", - "ZTNA21817", - "ZTNA21819", - "CISAMSEXO111", - "CISAMSEXO112", - "CISAMSEXO113" - ], - "helpText": "This creates a Anti-Phishing policy that automatically enables Mailbox Intelligence and spoofing, optional switches for Mail tips.", - "addedComponent": [ - { - "type": "textField", - "name": "standards.AntiPhishPolicy.name", - "label": "Policy Name", - "required": true, - "defaultValue": "CIPP Default Anti-Phishing Policy" - }, + { "type": "textField", "name": "standards.SafeLinksPolicy.name", "label": "Policy Name", "required": true, "defaultValue": "CIPP Default SafeLinks Policy" }, + { "type": "switch", "label": "AllowClickThrough", "name": "standards.SafeLinksPolicy.AllowClickThrough" }, + { "type": "switch", "label": "DisableUrlRewrite", "name": "standards.SafeLinksPolicy.DisableUrlRewrite" }, + { "type": "switch", "label": "EnableOrganizationBranding", "name": "standards.SafeLinksPolicy.EnableOrganizationBranding" }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "name": "standards.SafeLinksPolicy.DoNotRewriteUrls", "label": "Do not rewrite the following URLs in email" } + ], + "label": "Default Safe Links Policy", + "impact": "Low Impact", + "impactColour": "info", + "addedDate": "2024-03-25", + "powershellEquivalent": "Set-SafeLinksPolicy or New-SafeLinksPolicy", + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] + }, + { + "name": "standards.AntiPhishPolicy", + "cat": "Defender Standards", + "tag": ["mdo_safeattachments", "mdo_highconfidencespamaction", "mdo_highconfidencephishaction", "mdo_phisspamacation", "mdo_spam_notifications_only_for_admins", "mdo_antiphishingpolicies", "mdo_phishthresholdlevel", "CIS M365 5.0 (2.1.7)", "NIST CSF 2.0 (DE.CM-09)", "ORCA104", "ORCA115", "ORCA180", "ORCA220", "ORCA221", "ORCA222", "ORCA223", "ORCA228", "ORCA229", "ORCA230", "ORCA233", "ORCA234", "ORCA235", "ORCA239", "ORCA242", "ORCA243", "ORCA244", "ZTNA21784", "ZTNA21817", "ZTNA21819", "CISAMSEXO111", "CISAMSEXO112", "CISAMSEXO113"], + "helpText": "This creates a Anti-Phishing policy that automatically enables Mailbox Intelligence and spoofing, optional switches for Mail tips.", + "addedComponent": [ + { "type": "textField", "name": "standards.AntiPhishPolicy.name", "label": "Policy Name", "required": true, "defaultValue": "CIPP Default Anti-Phishing Policy" }, { "type": "number", "label": "Phishing email threshold. (Default 1)", @@ -2704,44 +2087,18 @@ "max": { "value": 4, "message": "Maximum value is 4" } } }, - { - "type": "switch", - "label": "Show first contact safety tip", - "name": "standards.AntiPhishPolicy.EnableFirstContactSafetyTips", - "defaultValue": true - }, - { - "type": "switch", - "label": "Show user impersonation safety tip", - "name": "standards.AntiPhishPolicy.EnableSimilarUsersSafetyTips", - "defaultValue": true - }, - { - "type": "switch", - "label": "Show domain impersonation safety tip", - "name": "standards.AntiPhishPolicy.EnableSimilarDomainsSafetyTips", - "defaultValue": true - }, - { - "type": "switch", - "label": "Show user impersonation unusual characters safety tip", - "name": "standards.AntiPhishPolicy.EnableUnusualCharactersSafetyTips", - "defaultValue": true - }, + { "type": "switch", "label": "Show first contact safety tip", "name": "standards.AntiPhishPolicy.EnableFirstContactSafetyTips", "defaultValue": true }, + { "type": "switch", "label": "Show user impersonation safety tip", "name": "standards.AntiPhishPolicy.EnableSimilarUsersSafetyTips", "defaultValue": true }, + { "type": "switch", "label": "Show domain impersonation safety tip", "name": "standards.AntiPhishPolicy.EnableSimilarDomainsSafetyTips", "defaultValue": true }, + { "type": "switch", "label": "Show user impersonation unusual characters safety tip", "name": "standards.AntiPhishPolicy.EnableUnusualCharactersSafetyTips", "defaultValue": true }, { "type": "select", "multiple": false, "label": "If the message is detected as spoof by spoof intelligence", "name": "standards.AntiPhishPolicy.AuthenticationFailAction", "options": [ - { - "label": "Quarantine the message", - "value": "Quarantine" - }, - { - "label": "Move to Junk Folder", - "value": "MoveToJmf" - } + { "label": "Quarantine the message", "value": "Quarantine" }, + { "label": "Move to Junk Folder", "value": "MoveToJmf" } ] }, { @@ -2751,18 +2108,9 @@ "label": "Quarantine policy for Spoof", "name": "standards.AntiPhishPolicy.SpoofQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, { @@ -2771,18 +2119,9 @@ "label": "If a message is detected as user impersonation", "name": "standards.AntiPhishPolicy.TargetedUserProtectionAction", "options": [ - { - "label": "Move to Junk Folder", - "value": "MoveToJmf" - }, - { - "label": "Delete the message before its delivered", - "value": "Delete" - }, - { - "label": "Quarantine the message", - "value": "Quarantine" - } + { "label": "Move to Junk Folder", "value": "MoveToJmf" }, + { "label": "Delete the message before its delivered", "value": "Delete" }, + { "label": "Quarantine the message", "value": "Quarantine" } ] }, { @@ -2792,18 +2131,9 @@ "label": "Quarantine policy for user impersonation", "name": "standards.AntiPhishPolicy.TargetedUserQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, { @@ -2812,18 +2142,9 @@ "label": "If a message is detected as domain impersonation", "name": "standards.AntiPhishPolicy.TargetedDomainProtectionAction", "options": [ - { - "label": "Move to Junk Folder", - "value": "MoveToJmf" - }, - { - "label": "Delete the message before its delivered", - "value": "Delete" - }, - { - "label": "Quarantine the message", - "value": "Quarantine" - } + { "label": "Move to Junk Folder", "value": "MoveToJmf" }, + { "label": "Delete the message before its delivered", "value": "Delete" }, + { "label": "Quarantine the message", "value": "Quarantine" } ] }, { @@ -2833,18 +2154,9 @@ "label": "Quarantine policy for domain impersonation", "name": "standards.AntiPhishPolicy.TargetedDomainQuarantineTag", "options": [ - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - }, - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - } + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" }, + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" } ] }, { @@ -2853,18 +2165,9 @@ "label": "If Mailbox Intelligence detects an impersonated user", "name": "standards.AntiPhishPolicy.MailboxIntelligenceProtectionAction", "options": [ - { - "label": "Move to Junk Folder", - "value": "MoveToJmf" - }, - { - "label": "Delete the message before its delivered", - "value": "Delete" - }, - { - "label": "Quarantine the message", - "value": "Quarantine" - } + { "label": "Move to Junk Folder", "value": "MoveToJmf" }, + { "label": "Delete the message before its delivered", "value": "Delete" }, + { "label": "Quarantine the message", "value": "Quarantine" } ] }, { @@ -2874,18 +2177,9 @@ "label": "Apply quarantine policy", "name": "standards.AntiPhishPolicy.MailboxIntelligenceQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] } ], @@ -2894,47 +2188,25 @@ "impactColour": "info", "addedDate": "2024-03-25", "powershellEquivalent": "Set-AntiPhishPolicy or New-AntiPhishPolicy", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SafeAttachmentPolicy", "cat": "Defender Standards", - "tag": [ - "CIS M365 5.0 (2.1.4)", - "mdo_safedocuments", - "mdo_commonattachmentsfilter", - "mdo_safeattachmentpolicy", - "NIST CSF 2.0 (DE.CM-09)", - "ORCA158", - "ORCA227" - ], + "tag": ["CIS M365 5.0 (2.1.4)", "mdo_safedocuments", "mdo_commonattachmentsfilter", "mdo_safeattachmentpolicy", "NIST CSF 2.0 (DE.CM-09)", "ORCA158", "ORCA227"], "helpText": "This creates a Safe Attachment policy", "addedComponent": [ - { - "type": "textField", - "name": "standards.SafeAttachmentPolicy.name", - "label": "Policy Name", - "required": true, - "defaultValue": "CIPP Default Safe Attachment Policy" - }, + { "type": "textField", "name": "standards.SafeAttachmentPolicy.name", "label": "Policy Name", "required": true, "defaultValue": "CIPP Default Safe Attachment Policy" }, { "type": "select", "multiple": false, "label": "Safe Attachment Action", "name": "standards.SafeAttachmentPolicy.SafeAttachmentAction", "options": [ - { - "label": "Allow", - "value": "Allow" - }, - { - "label": "Block", - "value": "Block" - }, - { - "label": "DynamicDelivery", - "value": "DynamicDelivery" - } + { "label": "Allow", "value": "Allow" }, + { "label": "Block", "value": "Block" }, + { "label": "DynamicDelivery", "value": "DynamicDelivery" } ] }, { @@ -2944,35 +2216,18 @@ "label": "QuarantineTag", "name": "standards.SafeAttachmentPolicy.QuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, - { - "type": "switch", - "label": "Redirect", - "name": "standards.SafeAttachmentPolicy.Redirect" - }, + { "type": "switch", "label": "Redirect", "name": "standards.SafeAttachmentPolicy.Redirect" }, { "type": "textField", "name": "standards.SafeAttachmentPolicy.RedirectAddress", "label": "Redirect Address", "required": false, - "condition": { - "field": "standards.SafeAttachmentPolicy.Redirect", - "compareType": "is", - "compareValue": true - } + "condition": { "field": "standards.SafeAttachmentPolicy.Redirect", "compareType": "is", "compareValue": true } } ], "label": "Default Safe Attachment Policy", @@ -2980,7 +2235,8 @@ "impactColour": "info", "addedDate": "2024-03-25", "powershellEquivalent": "Set-SafeAttachmentPolicy or New-SafeAttachmentPolicy", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.AtpPolicyForO365", @@ -2988,20 +2244,15 @@ "tag": ["CIS M365 5.0 (2.1.5)", "NIST CSF 2.0 (DE.CM-09)"], "helpText": "This creates a Atp policy that enables Defender for Office 365 for SharePoint, OneDrive and Microsoft Teams.", "addedComponent": [ - { - "type": "switch", - "label": "Allow people to click through Protected View even if Safe Documents identified the file as malicious", - "name": "standards.AtpPolicyForO365.AllowSafeDocsOpen", - "defaultValue": false, - "required": false - } + { "type": "switch", "label": "Allow people to click through Protected View even if Safe Documents identified the file as malicious", "name": "standards.AtpPolicyForO365.AllowSafeDocsOpen", "defaultValue": false, "required": false } ], "label": "Default Atp Policy For O365", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-03-25", "powershellEquivalent": "Set-AtpPolicyForO365", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.PhishingSimulations", @@ -3009,96 +2260,37 @@ "tag": [], "helpText": "This creates a phishing simulation policy that enables phishing simulations for the entire tenant.", "addedComponent": [ - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": true, - "label": "Phishing Simulation Domains", - "name": "standards.PhishingSimulations.Domains" - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": true, - "label": "Phishing Simulation Sender IP Ranges", - "name": "standards.PhishingSimulations.SenderIpRanges" - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "label": "Phishing Simulation Urls", - "name": "standards.PhishingSimulations.PhishingSimUrls" - }, - { - "type": "switch", - "label": "Remove extra urls", - "name": "standards.PhishingSimulations.RemoveExtraUrls", - "defaultValue": false, - "required": false - } + { "type": "autoComplete", "multiple": true, "creatable": true, "required": true, "label": "Phishing Simulation Domains", "name": "standards.PhishingSimulations.Domains" }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": true, "label": "Phishing Simulation Sender IP Ranges", "name": "standards.PhishingSimulations.SenderIpRanges" }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "label": "Phishing Simulation Urls", "name": "standards.PhishingSimulations.PhishingSimUrls" }, + { "type": "switch", "label": "Remove extra urls", "name": "standards.PhishingSimulations.RemoveExtraUrls", "defaultValue": false, "required": false } ], "label": "Phishing Simulation Configuration", "impact": "Medium Impact", "impactColour": "info", "addedDate": "2025-03-27", "powershellEquivalent": "New-TenantAllowBlockListItems, New-PhishSimOverridePolicy and New-ExoPhishSimOverrideRule", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.MalwareFilterPolicy", "cat": "Defender Standards", - "tag": [ - "CIS M365 5.0 (2.1.2)", - "CIS M365 5.0 (2.1.3)", - "mdo_zapspam", - "mdo_zapphish", - "mdo_zapmalware", - "NIST CSF 2.0 (DE.CM-09)", - "ORCA121", - "ORCA124", - "ORCA232", - "ZTNA21817", - "ZTNA21819", - "CISAMSEXO95", - "CISAMSEXO101", - "CISAMSEXO102", - "CISAMSEXO103" - ], + "tag": ["CIS M365 5.0 (2.1.2)", "CIS M365 5.0 (2.1.3)", "mdo_zapspam", "mdo_zapphish", "mdo_zapmalware", "NIST CSF 2.0 (DE.CM-09)", "ORCA121", "ORCA124", "ORCA232", "ZTNA21817", "ZTNA21819", "CISAMSEXO95", "CISAMSEXO101", "CISAMSEXO102", "CISAMSEXO103"], "helpText": "This creates a Malware filter policy that enables the default File filter and Zero-hour auto purge for malware.", "addedComponent": [ - { - "type": "textField", - "name": "standards.MalwareFilterPolicy.name", - "label": "Policy Name", - "required": true, - "defaultValue": "CIPP Default Malware Policy" - }, + { "type": "textField", "name": "standards.MalwareFilterPolicy.name", "label": "Policy Name", "required": true, "defaultValue": "CIPP Default Malware Policy" }, { "type": "select", "multiple": false, "label": "FileTypeAction", "name": "standards.MalwareFilterPolicy.FileTypeAction", "options": [ - { - "label": "Reject", - "value": "Reject" - }, - { - "label": "Quarantine the message", - "value": "Quarantine" - } + { "label": "Reject", "value": "Reject" }, + { "label": "Quarantine the message", "value": "Quarantine" } ] }, - { - "type": "textField", - "name": "standards.MalwareFilterPolicy.OptionalFileTypes", - "required": false, - "label": "Optional File Types, Comma separated" - }, + { "type": "textField", "name": "standards.MalwareFilterPolicy.OptionalFileTypes", "required": false, "label": "Optional File Types, Comma separated" }, { "type": "select", "multiple": false, @@ -3106,53 +2298,26 @@ "label": "QuarantineTag", "name": "standards.MalwareFilterPolicy.QuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, - { - "type": "switch", - "label": "Enable Internal Sender Admin Notifications", - "required": false, - "name": "standards.MalwareFilterPolicy.EnableInternalSenderAdminNotifications" - }, + { "type": "switch", "label": "Enable Internal Sender Admin Notifications", "required": false, "name": "standards.MalwareFilterPolicy.EnableInternalSenderAdminNotifications" }, { "type": "textField", "name": "standards.MalwareFilterPolicy.InternalSenderAdminAddress", "required": false, "label": "Internal Sender Admin Address", - "condition": { - "field": "standards.MalwareFilterPolicy.EnableInternalSenderAdminNotifications", - "compareType": "is", - "compareValue": true - } - }, - { - "type": "switch", - "label": "Enable External Sender Admin Notifications", - "required": false, - "name": "standards.MalwareFilterPolicy.EnableExternalSenderAdminNotifications" + "condition": { "field": "standards.MalwareFilterPolicy.EnableInternalSenderAdminNotifications", "compareType": "is", "compareValue": true } }, + { "type": "switch", "label": "Enable External Sender Admin Notifications", "required": false, "name": "standards.MalwareFilterPolicy.EnableExternalSenderAdminNotifications" }, { "type": "textField", "name": "standards.MalwareFilterPolicy.ExternalSenderAdminAddress", "required": false, "label": "External Sender Admin Address", - "condition": { - "field": "standards.MalwareFilterPolicy.EnableExternalSenderAdminNotifications", - "compareType": "is", - "compareValue": true - } + "condition": { "field": "standards.MalwareFilterPolicy.EnableExternalSenderAdminNotifications", "compareType": "is", "compareValue": true } } ], "label": "Default Malware Filter Policy", @@ -3160,7 +2325,8 @@ "impactColour": "info", "addedDate": "2024-03-25", "powershellEquivalent": "Set-MalwareFilterPolicy or New-MalwareFilterPolicy", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.PhishSimSpoofIntelligence", @@ -3168,61 +2334,25 @@ "tag": [], "helpText": "This adds allowed domains to the Spoof Intelligence Allow/Block List.", "addedComponent": [ - { - "type": "switch", - "label": "Remove extra domains from the allow list", - "name": "standards.PhishSimSpoofIntelligence.RemoveExtraDomains", - "defaultValue": false, - "required": false - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "label": "Allowed Domains", - "name": "standards.PhishSimSpoofIntelligence.AllowedDomains" - } + { "type": "switch", "label": "Remove extra domains from the allow list", "name": "standards.PhishSimSpoofIntelligence.RemoveExtraDomains", "defaultValue": false, "required": false }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "label": "Allowed Domains", "name": "standards.PhishSimSpoofIntelligence.AllowedDomains" } ], "label": "Add allowed domains to Spoof Intelligence", "impact": "Medium Impact", "impactColour": "info", "addedDate": "2025-03-28", "powershellEquivalent": "New-TenantAllowBlockListSpoofItems", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.SpamFilterPolicy", "cat": "Defender Standards", - "tag": [ - "ORCA100", - "ORCA101", - "ORCA102", - "ORCA103", - "ORCA104", - "ORCA123", - "ORCA139", - "ORCA140", - "ORCA141", - "ORCA142", - "ORCA143", - "ORCA224", - "ORCA231", - "ORCA241", - "CISAMSEXO141", - "CISAMSEXO142", - "CISAMSEXO143" - ], + "tag": ["ORCA100", "ORCA101", "ORCA102", "ORCA103", "ORCA104", "ORCA123", "ORCA139", "ORCA140", "ORCA141", "ORCA142", "ORCA143", "ORCA224", "ORCA231", "ORCA241", "CISAMSEXO141", "CISAMSEXO142", "CISAMSEXO143"], "helpText": "This standard creates a Spam filter policy similar to the default strict policy.", "docsDescription": "This standard creates a Spam filter policy similar to the default strict policy, the following settings are configured to on by default: IncreaseScoreWithNumericIps, IncreaseScoreWithRedirectToOtherPort, MarkAsSpamEmptyMessages, MarkAsSpamJavaScriptInHtml, MarkAsSpamSpfRecordHardFail, MarkAsSpamFromAddressAuthFail, MarkAsSpamNdrBackscatter, MarkAsSpamBulkMail, InlineSafetyTipsEnabled, PhishZapEnabled, SpamZapEnabled", "addedComponent": [ - { - "type": "textField", - "name": "standards.SpamFilterPolicy.name", - "label": "Policy Name", - "required": true, - "defaultValue": "CIPP Default Spam Filter Policy" - }, + { "type": "textField", "name": "standards.SpamFilterPolicy.name", "label": "Policy Name", "required": true, "defaultValue": "CIPP Default Spam Filter Policy" }, { "type": "number", "label": "Bulk email threshold (Default 7)", @@ -3241,14 +2371,8 @@ "label": "Spam Action", "name": "standards.SpamFilterPolicy.SpamAction", "options": [ - { - "label": "Quarantine the message", - "value": "Quarantine" - }, - { - "label": "Move message to Junk Email folder", - "value": "MoveToJmf" - } + { "label": "Quarantine the message", "value": "Quarantine" }, + { "label": "Move message to Junk Email folder", "value": "MoveToJmf" } ] }, { @@ -3259,18 +2383,9 @@ "label": "Spam Quarantine Tag", "name": "standards.SpamFilterPolicy.SpamQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, { @@ -3281,14 +2396,8 @@ "label": "High Confidence Spam Action", "name": "standards.SpamFilterPolicy.HighConfidenceSpamAction", "options": [ - { - "label": "Quarantine the message", - "value": "Quarantine" - }, - { - "label": "Move message to Junk Email folder", - "value": "MoveToJmf" - } + { "label": "Quarantine the message", "value": "Quarantine" }, + { "label": "Move message to Junk Email folder", "value": "MoveToJmf" } ] }, { @@ -3299,18 +2408,9 @@ "label": "High Confidence Spam Quarantine Tag", "name": "standards.SpamFilterPolicy.HighConfidenceSpamQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, { @@ -3321,14 +2421,8 @@ "label": "Bulk Spam Action", "name": "standards.SpamFilterPolicy.BulkSpamAction", "options": [ - { - "label": "Quarantine the message", - "value": "Quarantine" - }, - { - "label": "Move message to Junk Email folder", - "value": "MoveToJmf" - } + { "label": "Quarantine the message", "value": "Quarantine" }, + { "label": "Move message to Junk Email folder", "value": "MoveToJmf" } ] }, { @@ -3339,18 +2433,9 @@ "label": "Bulk Quarantine Tag", "name": "standards.SpamFilterPolicy.BulkQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, { @@ -3361,14 +2446,8 @@ "label": "Phish Spam Action", "name": "standards.SpamFilterPolicy.PhishSpamAction", "options": [ - { - "label": "Quarantine the message", - "value": "Quarantine" - }, - { - "label": "Move message to Junk Email folder", - "value": "MoveToJmf" - } + { "label": "Quarantine the message", "value": "Quarantine" }, + { "label": "Move message to Junk Email folder", "value": "MoveToJmf" } ] }, { @@ -3379,18 +2458,9 @@ "label": "Phish Quarantine Tag", "name": "standards.SpamFilterPolicy.PhishQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, { @@ -3401,74 +2471,20 @@ "label": "High Confidence Phish Quarantine Tag", "name": "standards.SpamFilterPolicy.HighConfidencePhishQuarantineTag", "options": [ - { - "label": "AdminOnlyAccessPolicy", - "value": "AdminOnlyAccessPolicy" - }, - { - "label": "DefaultFullAccessPolicy", - "value": "DefaultFullAccessPolicy" - }, - { - "label": "DefaultFullAccessWithNotificationPolicy", - "value": "DefaultFullAccessWithNotificationPolicy" - } + { "label": "AdminOnlyAccessPolicy", "value": "AdminOnlyAccessPolicy" }, + { "label": "DefaultFullAccessPolicy", "value": "DefaultFullAccessPolicy" }, + { "label": "DefaultFullAccessWithNotificationPolicy", "value": "DefaultFullAccessWithNotificationPolicy" } ] }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.IncreaseScoreWithImageLinks", - "label": "Increase score if message contains image links to remote websites", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.IncreaseScoreWithBizOrInfoUrls", - "label": "Increase score if message contains links to .biz or .info domains", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.MarkAsSpamFramesInHtml", - "label": "Mark as spam if message contains HTML or iframe tags", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.MarkAsSpamObjectTagsInHtml", - "label": "Mark as spam if message contains HTML object tags", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.MarkAsSpamEmbedTagsInHtml", - "label": "Mark as spam if message contains HTML embed tags", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.MarkAsSpamFormTagsInHtml", - "label": "Mark as spam if message contains HTML form tags", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.MarkAsSpamWebBugsInHtml", - "label": "Mark as spam if message contains web bugs (also known as web beacons)", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.MarkAsSpamSensitiveWordList", - "label": "Mark as spam if message contains words from the sensitive words list", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.EnableLanguageBlockList", - "label": "Enable language block list", - "defaultValue": false - }, + { "type": "switch", "name": "standards.SpamFilterPolicy.IncreaseScoreWithImageLinks", "label": "Increase score if message contains image links to remote websites", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.IncreaseScoreWithBizOrInfoUrls", "label": "Increase score if message contains links to .biz or .info domains", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.MarkAsSpamFramesInHtml", "label": "Mark as spam if message contains HTML or iframe tags", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.MarkAsSpamObjectTagsInHtml", "label": "Mark as spam if message contains HTML object tags", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.MarkAsSpamEmbedTagsInHtml", "label": "Mark as spam if message contains HTML embed tags", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.MarkAsSpamFormTagsInHtml", "label": "Mark as spam if message contains HTML form tags", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.MarkAsSpamWebBugsInHtml", "label": "Mark as spam if message contains web bugs (also known as web beacons)", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.MarkAsSpamSensitiveWordList", "label": "Mark as spam if message contains words from the sensitive words list", "defaultValue": false }, + { "type": "switch", "name": "standards.SpamFilterPolicy.EnableLanguageBlockList", "label": "Enable language block list", "defaultValue": false }, { "type": "autoComplete", "multiple": true, @@ -3476,18 +2492,9 @@ "required": false, "name": "standards.SpamFilterPolicy.LanguageBlockList", "label": "Languages to block (uppercase ISO 639-1 two-letter)", - "condition": { - "field": "standards.SpamFilterPolicy.EnableLanguageBlockList", - "compareType": "is", - "compareValue": true - } - }, - { - "type": "switch", - "name": "standards.SpamFilterPolicy.EnableRegionBlockList", - "label": "Enable region block list", - "defaultValue": false + "condition": { "field": "standards.SpamFilterPolicy.EnableLanguageBlockList", "compareType": "is", "compareValue": true } }, + { "type": "switch", "name": "standards.SpamFilterPolicy.EnableRegionBlockList", "label": "Enable region block list", "defaultValue": false }, { "type": "autoComplete", "multiple": true, @@ -3495,106 +2502,43 @@ "required": false, "name": "standards.SpamFilterPolicy.RegionBlockList", "label": "Regions to block (uppercase ISO 3166-1 two-letter)", - "condition": { - "field": "standards.SpamFilterPolicy.EnableRegionBlockList", - "compareType": "is", - "compareValue": true - } + "condition": { "field": "standards.SpamFilterPolicy.EnableRegionBlockList", "compareType": "is", "compareValue": true } }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "name": "standards.SpamFilterPolicy.AllowedSenderDomains", - "label": "Allowed sender domains" - } + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "name": "standards.SpamFilterPolicy.AllowedSenderDomains", "label": "Allowed sender domains" } ], "label": "Default Spam Filter Policy", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2024-07-15", "powershellEquivalent": "New-HostedContentFilterPolicy or Set-HostedContentFilterPolicy", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.QuarantineTemplate", "cat": "Defender Standards", - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "tag": [], "helpText": "This standard creates a Custom Quarantine Policies that can be used in Anti-Spam and all MDO365 policies. Quarantine Policies can be used to specify recipients permissions, enable end-user spam notifications, and specify the release action preference", "executiveText": "Creates standardized quarantine policies that define how employees can interact with quarantined emails, including permissions to release, delete, or preview suspicious messages. This ensures consistent security handling across the organization while providing appropriate user access to manage quarantined content.", "addedComponent": [ - { - "type": "autoComplete", - "multiple": false, - "creatable": true, - "name": "displayName", - "label": "Quarantine Display Name", - "required": true - }, - { - "type": "switch", - "label": "Enable end-user spam notifications", - "name": "ESNEnabled", - "defaultValue": true, - "required": false - }, + { "type": "autoComplete", "multiple": false, "creatable": true, "name": "displayName", "label": "Quarantine Display Name", "required": true }, + { "type": "switch", "label": "Enable end-user spam notifications", "name": "ESNEnabled", "defaultValue": true, "required": false }, { "type": "select", "multiple": false, "label": "Select release action preference", "name": "ReleaseAction", "options": [ - { - "label": "Allow recipients to request a message to be released from quarantine", - "value": "PermissionToRequestRelease" - }, - { - "label": "Allow recipients to release a message from quarantine", - "value": "PermissionToRelease" - } + { "label": "Allow recipients to request a message to be released from quarantine", "value": "PermissionToRequestRelease" }, + { "label": "Allow recipients to release a message from quarantine", "value": "PermissionToRelease" } ] }, - { - "type": "switch", - "label": "Include Messages From Blocked Sender Address", - "name": "IncludeMessagesFromBlockedSenderAddress", - "defaultValue": false, - "required": false - }, - { - "type": "switch", - "label": "Allow recipients to delete message", - "name": "PermissionToDelete", - "defaultValue": false, - "required": false - }, - { - "type": "switch", - "label": "Allow recipients to preview message", - "name": "PermissionToPreview", - "defaultValue": false, - "required": false - }, - { - "type": "switch", - "label": "Allow recipients to block Sender Address", - "name": "PermissionToBlockSender", - "defaultValue": false, - "required": false - }, - { - "type": "switch", - "label": "Allow recipients to whitelist Sender Address", - "name": "PermissionToAllowSender", - "defaultValue": false, - "required": false - } + { "type": "switch", "label": "Include Messages From Blocked Sender Address", "name": "IncludeMessagesFromBlockedSenderAddress", "defaultValue": false, "required": false }, + { "type": "switch", "label": "Allow recipients to delete message", "name": "PermissionToDelete", "defaultValue": false, "required": false }, + { "type": "switch", "label": "Allow recipients to preview message", "name": "PermissionToPreview", "defaultValue": false, "required": false }, + { "type": "switch", "label": "Allow recipients to block Sender Address", "name": "PermissionToBlockSender", "defaultValue": false, "required": false }, + { "type": "switch", "label": "Allow recipients to whitelist Sender Address", "name": "PermissionToAllowSender", "defaultValue": false, "required": false } ], "label": "Custom Quarantine Policy", "multiple": true, @@ -3602,7 +2546,8 @@ "impactColour": "info", "addedDate": "2025-05-16", "powershellEquivalent": "Set-QuarantinePolicy or New-QuarantinePolicy", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.IntuneWindowsDiagnostic", @@ -3612,25 +2557,16 @@ "docsDescription": "Enables Windows diagnostic data in processor configuration for your Intune tenant. This setting is required for several Intune features including Windows feature update device readiness reports, compatibility risk reports, driver update reports, and update policy alerts. When enabled, your organization becomes the controller of Windows diagnostic data collected from managed devices, allowing Intune to use this data for reporting and update management features. More information can be found in [Microsoft's documentation.](https://go.microsoft.com/fwlink/?linkid=2204384)", "executiveText": "Enables access to Windows Update reporting and compatibility analysis features in Intune by allowing the use of Windows diagnostic data. This unlocks important capabilities like device readiness reports for feature updates, driver update reports, and proactive alerts for update failures, helping IT teams plan and monitor Windows updates more effectively across the organization.", "addedComponent": [ - { - "type": "switch", - "name": "standards.IntuneWindowsDiagnostic.areDataProcessorServiceForWindowsFeaturesEnabled", - "label": "Enable Windows data", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.IntuneWindowsDiagnostic.hasValidWindowsLicense", - "label": "Confirm ownership of the required Windows E3 or equivalent licenses (Enables Windows update app and driver compatibility reports)", - "defaultValue": false - } + { "type": "switch", "name": "standards.IntuneWindowsDiagnostic.areDataProcessorServiceForWindowsFeaturesEnabled", "label": "Enable Windows data", "defaultValue": false }, + { "type": "switch", "name": "standards.IntuneWindowsDiagnostic.hasValidWindowsLicense", "label": "Confirm ownership of the required Windows E3 or equivalent licenses (Enables Windows update app and driver compatibility reports)", "defaultValue": false } ], "label": "Set Intune Windows diagnostic data settings", "impact": "Low Impact", "impactColour": "info", "addedDate": "2026-01-27", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.WindowsBackupRestore", @@ -3658,7 +2594,8 @@ "impactColour": "info", "addedDate": "2026-02-26", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.intuneDeviceRetirementDays", @@ -3667,18 +2604,15 @@ "helpText": "A value between 31 and 365 is supported. retired devices are removed from Intune after the specified number of days.", "executiveText": "Automatically removes inactive devices from management after a specified period, helping maintain a clean device inventory and reducing security risks from abandoned or lost devices. This policy ensures that only actively used corporate devices remain in the management system.", "addedComponent": [ - { - "type": "number", - "name": "standards.intuneDeviceRetirementDays.days", - "label": "Maximum days" - } + { "type": "number", "name": "standards.intuneDeviceRetirementDays.days", "label": "Maximum days" } ], "label": "Set inactive device retirement days", "impact": "Low Impact", "impactColour": "info", "addedDate": "2023-05-19", "powershellEquivalent": "Graph API", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.intuneBrandingProfile", @@ -3687,72 +2621,24 @@ "helpText": "Sets the branding profile for the Intune Company Portal app. This is a tenant wide setting and overrules any settings set on the app level.", "executiveText": "Customizes the Intune Company Portal app with company branding, contact information, and support details, providing employees with a consistent corporate experience when managing their devices. This improves user experience and ensures employees know how to get IT support when needed.", "addedComponent": [ - { - "type": "textField", - "name": "standards.intuneBrandingProfile.displayName", - "label": "Organization name", - "required": false - }, - { - "type": "switch", - "name": "standards.intuneBrandingProfile.showLogo", - "label": "Show logo" - }, - { - "type": "switch", - "name": "standards.intuneBrandingProfile.showDisplayNameNextToLogo", - "label": "Show organization name next to logo", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.contactITName", - "label": "Contact IT name", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.contactITPhoneNumber", - "label": "Contact IT phone number", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.contactITEmailAddress", - "label": "Contact IT email address", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.contactITNotes", - "label": "Contact IT notes", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.onlineSupportSiteName", - "label": "Online support site name", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.onlineSupportSiteUrl", - "label": "Online support site URL", - "required": false - }, - { - "type": "textField", - "name": "standards.intuneBrandingProfile.privacyUrl", - "label": "Privacy statement URL", - "required": false - } + { "type": "textField", "name": "standards.intuneBrandingProfile.displayName", "label": "Organization name", "required": false }, + { "type": "switch", "name": "standards.intuneBrandingProfile.showLogo", "label": "Show logo" }, + { "type": "switch", "name": "standards.intuneBrandingProfile.showDisplayNameNextToLogo", "label": "Show organization name next to logo", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.contactITName", "label": "Contact IT name", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.contactITPhoneNumber", "label": "Contact IT phone number", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.contactITEmailAddress", "label": "Contact IT email address", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.contactITNotes", "label": "Contact IT notes", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.onlineSupportSiteName", "label": "Online support site name", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.onlineSupportSiteUrl", "label": "Online support site URL", "required": false }, + { "type": "textField", "name": "standards.intuneBrandingProfile.privacyUrl", "label": "Privacy statement URL", "required": false } ], "label": "Set Intune Company Portal branding profile", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-06-20", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.IntuneComplianceSettings", @@ -3769,14 +2655,8 @@ "name": "standards.IntuneComplianceSettings.secureByDefault", "label": "Mark devices with no compliance policy as", "options": [ - { - "label": "Compliant", - "value": "false" - }, - { - "label": "Non-Compliant", - "value": "true" - } + { "label": "Compliant", "value": "false" }, + { "label": "Non-Compliant", "value": "true" } ] }, { @@ -3795,7 +2675,8 @@ "impactColour": "info", "addedDate": "2024-11-12", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.MDMScope", @@ -3810,33 +2691,20 @@ "label": "MDM User Scope?", "type": "radio", "options": [ - { - "label": "All", - "value": "all" - }, - { - "label": "None", - "value": "none" - }, - { - "label": "Custom Group", - "value": "selected" - } + { "label": "All", "value": "all" }, + { "label": "None", "value": "none" }, + { "label": "Custom Group", "value": "selected" } ] }, - { - "type": "textField", - "name": "standards.MDMScope.customGroup", - "label": "Custom Group Name", - "required": false - } + { "type": "textField", "name": "standards.MDMScope.customGroup", "label": "Custom Group Name", "required": false } ], "label": "Configure MDM user scope", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-02-18", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.DefaultPlatformRestrictions", @@ -3845,73 +2713,24 @@ "helpText": "Sets the default platform restrictions for enrolling devices into Intune. Note: Do not block personally owned if platform is blocked.", "executiveText": "Controls which types of devices (iOS, Android, Windows, macOS) and ownership models (corporate vs. personal) can be enrolled in the company's device management system. This helps maintain security standards while supporting necessary business device types and usage scenarios.", "addedComponent": [ - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.platformAndroidForWorkBlocked", - "label": "Block platform Android Enterprise (work profile)", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.personalAndroidForWorkBlocked", - "label": "Block personally owned Android Enterprise (work profile)", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.platformAndroidBlocked", - "label": "Block platform Android", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.personalAndroidBlocked", - "label": "Block personally owned Android", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.platformiOSBlocked", - "label": "Block platform iOS", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.personaliOSBlocked", - "label": "Block personally owned iOS", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.platformMacOSBlocked", - "label": "Block platform macOS", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.personalMacOSBlocked", - "label": "Block personally owned macOS", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.platformWindowsBlocked", - "label": "Block platform Windows", - "default": false - }, - { - "type": "switch", - "name": "standards.DefaultPlatformRestrictions.personalWindowsBlocked", - "label": "Block personally owned Windows", - "default": false - } + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.platformAndroidForWorkBlocked", "label": "Block platform Android Enterprise (work profile)", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.personalAndroidForWorkBlocked", "label": "Block personally owned Android Enterprise (work profile)", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.platformAndroidBlocked", "label": "Block platform Android", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.personalAndroidBlocked", "label": "Block personally owned Android", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.platformiOSBlocked", "label": "Block platform iOS", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.personaliOSBlocked", "label": "Block personally owned iOS", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.platformMacOSBlocked", "label": "Block platform macOS", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.personalMacOSBlocked", "label": "Block personally owned macOS", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.platformWindowsBlocked", "label": "Block platform Windows", "default": false }, + { "type": "switch", "name": "standards.DefaultPlatformRestrictions.personalWindowsBlocked", "label": "Block personally owned Windows", "default": false } ], "label": "Device enrollment restrictions", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-04-01", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.MDMEnrollmentDuringRegistration", @@ -3921,18 +2740,15 @@ "docsDescription": "Controls whether Windows shows the \"Allow my organization to manage my device\" prompt when users add a work or school account. When set to disabled, this setting prevents automatic MDM enrollment during the account registration flow, separating account registration from device enrollment. This is useful for environments where you want to allow users to add work accounts without triggering MDM enrollment.", "executiveText": "Controls automatic device management enrollment during work account setup. When disabled, users can add work accounts to their Windows devices without the prompt asking to allow organizational device management, preventing unintended MDM enrollments on personal or BYOD devices.", "addedComponent": [ - { - "type": "switch", - "name": "standards.MDMEnrollmentDuringRegistration.disableEnrollment", - "label": "Disable MDM enrollment during registration" - } + { "type": "switch", "name": "standards.MDMEnrollmentDuringRegistration.disableEnrollment", "label": "Disable MDM enrollment during registration" } ], "label": "Configure MDM enrollment when adding work or school account", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2025-12-15", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration", @@ -3947,26 +2763,12 @@ "label": "Configure Windows Hello for Business", "multiple": false, "options": [ - { - "label": "Not configured", - "value": "notConfigured" - }, - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Not configured", "value": "notConfigured" }, + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, - { - "type": "switch", - "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.securityDeviceRequired", - "label": "Use a Trusted Platform Module (TPM)", - "default": true - }, + { "type": "switch", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.securityDeviceRequired", "label": "Use a Trusted Platform Module (TPM)", "default": true }, { "type": "number", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.pinMinimumLength", @@ -3993,18 +2795,9 @@ "label": "Lowercase letters in PIN", "multiple": false, "options": [ - { - "label": "Not allowed", - "value": "disallowed" - }, - { - "label": "Allowed", - "value": "allowed" - }, - { - "label": "Required", - "value": "required" - } + { "label": "Not allowed", "value": "disallowed" }, + { "label": "Allowed", "value": "allowed" }, + { "label": "Required", "value": "required" } ] }, { @@ -4013,18 +2806,9 @@ "label": "Uppercase letters in PIN", "multiple": false, "options": [ - { - "label": "Not allowed", - "value": "disallowed" - }, - { - "label": "Allowed", - "value": "allowed" - }, - { - "label": "Required", - "value": "required" - } + { "label": "Not allowed", "value": "disallowed" }, + { "label": "Allowed", "value": "allowed" }, + { "label": "Required", "value": "required" } ] }, { @@ -4033,71 +2817,34 @@ "label": "Special characters in PIN", "multiple": false, "options": [ - { - "label": "Not allowed", - "value": "disallowed" - }, - { - "label": "Allowed", - "value": "allowed" - }, - { - "label": "Required", - "value": "required" - } + { "label": "Not allowed", "value": "disallowed" }, + { "label": "Allowed", "value": "allowed" }, + { "label": "Required", "value": "required" } ] }, - { - "type": "number", - "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.pinExpirationInDays", - "label": "PIN expiration (days) - 0 to disable", - "default": 0 - }, - { - "type": "number", - "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.pinPreviousBlockCount", - "label": "PIN history - 0 to disable", - "default": 0 - }, - { - "type": "switch", - "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.unlockWithBiometricsEnabled", - "label": "Allow biometric authentication", - "default": true - }, + { "type": "number", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.pinExpirationInDays", "label": "PIN expiration (days) - 0 to disable", "default": 0 }, + { "type": "number", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.pinPreviousBlockCount", "label": "PIN history - 0 to disable", "default": 0 }, + { "type": "switch", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.unlockWithBiometricsEnabled", "label": "Allow biometric authentication", "default": true }, { "type": "autoComplete", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.enhancedBiometricsState", "label": "Use enhanced anti-spoofing when available", "multiple": false, "options": [ - { - "label": "Not configured", - "value": "notConfigured" - }, - { - "label": "Enabled", - "value": "enabled" - }, - { - "label": "Disabled", - "value": "disabled" - } + { "label": "Not configured", "value": "notConfigured" }, + { "label": "Enabled", "value": "enabled" }, + { "label": "Disabled", "value": "disabled" } ] }, - { - "type": "switch", - "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.remotePassportEnabled", - "label": "Allow phone sign-in", - "default": true - } + { "type": "switch", "name": "standards.EnrollmentWindowsHelloForBusinessConfiguration.remotePassportEnabled", "label": "Allow phone sign-in", "default": true } ], "label": "Windows Hello for Business enrollment configuration", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-09-25", "powershellEquivalent": "Graph API", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.intuneDeviceReg", @@ -4106,19 +2853,15 @@ "helpText": "Sets the maximum number of devices that can be registered by a user. A value of 0 disables device registration by users", "executiveText": "Limits how many devices each employee can register for corporate access, preventing excessive device proliferation while accommodating legitimate business needs. This helps maintain security oversight and prevents potential abuse of device registration privileges.", "addedComponent": [ - { - "type": "number", - "name": "standards.intuneDeviceReg.max", - "label": "Maximum devices (Enter 2147483647 for unlimited.)", - "required": true - } + { "type": "number", "name": "standards.intuneDeviceReg.max", "label": "Maximum devices (Enter 2147483647 for unlimited.)", "required": true } ], "label": "Set Maximum Number of Devices per user", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2023-03-27", "powershellEquivalent": "Update-MgBetaPolicyDeviceRegistrationPolicy", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.intuneDeviceRegLocalAdmins", @@ -4128,18 +2871,8 @@ "docsDescription": "Configures the Device Registration Policy local administrator behavior for registering users. When enabled, users who register devices are not granted local administrator rights, you can also configure if Global Administrators are added as local admins.", "executiveText": "Controls whether employees who enroll devices automatically receive local administrator access. Disabling registering-user admin rights follows least-privilege principles and reduces security risk from over-privileged endpoints.", "addedComponent": [ - { - "type": "switch", - "name": "standards.intuneDeviceRegLocalAdmins.disableRegisteringUsers", - "label": "Disable registering users as local administrators", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.intuneDeviceRegLocalAdmins.enableGlobalAdmins", - "label": "Allow Global Administrators to be local administrators", - "defaultValue": true - } + { "type": "switch", "name": "standards.intuneDeviceRegLocalAdmins.disableRegisteringUsers", "label": "Disable registering users as local administrators", "defaultValue": true }, + { "type": "switch", "name": "standards.intuneDeviceRegLocalAdmins.enableGlobalAdmins", "label": "Allow Global Administrators to be local administrators", "defaultValue": true } ], "label": "Configure local administrator rights for users joining devices", "impact": "Medium Impact", @@ -4156,12 +2889,7 @@ "docsDescription": "Configures whether users can register devices with Entra. When disabled, users are unable to register devices with Entra.", "executiveText": "Controls whether employees can register their devices for corporate access. Disabling user device registration prevents unauthorized or unmanaged devices from connecting to company resources, enhancing overall security posture.", "addedComponent": [ - { - "type": "switch", - "name": "standards.intuneRestrictUserDeviceRegistration.disableUserDeviceRegistration", - "label": "Disable users from registering devices", - "defaultValue": true - } + { "type": "switch", "name": "standards.intuneRestrictUserDeviceRegistration.disableUserDeviceRegistration", "label": "Disable users from registering devices", "defaultValue": true } ], "label": "Configure user restriction for Entra device registration", "impact": "High Impact", @@ -4197,54 +2925,18 @@ "name": "standards.DeletedUserRentention.Days", "label": "Retention time (Default 30 days)", "options": [ - { - "label": "30 days", - "value": "30" - }, - { - "label": "90 days", - "value": "90" - }, - { - "label": "1 year", - "value": "365" - }, - { - "label": "2 years", - "value": "730" - }, - { - "label": "3 years", - "value": "1095" - }, - { - "label": "4 years", - "value": "1460" - }, - { - "label": "5 years", - "value": "1825" - }, - { - "label": "6 years", - "value": "2190" - }, - { - "label": "7 years", - "value": "2555" - }, - { - "label": "8 years", - "value": "2920" - }, - { - "label": "9 years", - "value": "3285" - }, - { - "label": "10 years", - "value": "3650" - } + { "label": "30 days", "value": "30" }, + { "label": "90 days", "value": "90" }, + { "label": "1 year", "value": "365" }, + { "label": "2 years", "value": "730" }, + { "label": "3 years", "value": "1095" }, + { "label": "4 years", "value": "1460" }, + { "label": "5 years", "value": "1825" }, + { "label": "6 years", "value": "2190" }, + { "label": "7 years", "value": "2555" }, + { "label": "8 years", "value": "2920" }, + { "label": "9 years", "value": "3285" }, + { "label": "10 years", "value": "3650" } ] } ], @@ -4253,7 +2945,8 @@ "impactColour": "info", "addedDate": "2022-06-15", "powershellEquivalent": "Update-MgBetaAdminSharePointSetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPFileRequests", @@ -4263,11 +2956,7 @@ "docsDescription": "File Requests allow users to create secure upload-only share links where uploads are hidden from other people using the link. This creates a secure and private way for people to upload files to a folder. This feature is not enabled by default on new tenants and requires PowerShell configuration. This standard enables or disables this feature and optionally configures link expiration settings for both SharePoint and OneDrive.", "executiveText": "Enables secure file upload functionality that allows external users to submit files directly to company folders without seeing other submissions or folder contents. This provides a professional and secure way to collect documents from clients, vendors, and partners while maintaining data privacy and security.", "addedComponent": [ - { - "type": "switch", - "name": "standards.SPFileRequests.state", - "label": "Enable File Requests" - }, + { "type": "switch", "name": "standards.SPFileRequests.state", "label": "Enable File Requests" }, { "type": "number", "name": "standards.SPFileRequests.expirationDays", @@ -4284,7 +2973,8 @@ "impactColour": "warning", "addedDate": "2025-07-30", "powershellEquivalent": "Set-SPOTenant -CoreRequestFilesLinkEnabled $true -OneDriveRequestFilesLinkEnabled $true -CoreRequestFilesLinkExpirationInDays 30 -OneDriveRequestFilesLinkExpirationInDays 30", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.TenantDefaultTimezone", @@ -4293,18 +2983,15 @@ "helpText": "Sets the default timezone for the tenant. This will be used for all new users and sites.", "executiveText": "Standardizes the timezone setting across all SharePoint sites and new user accounts, ensuring consistent scheduling and time-based operations throughout the organization. This improves collaboration efficiency and reduces confusion in global or multi-timezone organizations.", "addedComponent": [ - { - "type": "TimezoneSelect", - "name": "standards.TenantDefaultTimezone.Timezone", - "label": "Timezone" - } + { "type": "TimezoneSelect", "name": "standards.TenantDefaultTimezone.Timezone", "label": "Timezone" } ], "label": "Set Default Timezone for Tenant", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-04-20", "powershellEquivalent": "Update-MgBetaAdminSharePointSetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPAzureB2B", @@ -4318,7 +3005,8 @@ "impactColour": "info", "addedDate": "2024-07-09", "powershellEquivalent": "Set-SPOTenant -EnableAzureADB2BIntegration $true", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPDisallowInfectedFiles", @@ -4332,7 +3020,8 @@ "impactColour": "info", "addedDate": "2024-07-09", "powershellEquivalent": "Set-SPOTenant -DisallowInfectedFileDownload $true", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPDisableLegacyWorkflows", @@ -4346,7 +3035,8 @@ "impactColour": "info", "addedDate": "2024-07-15", "powershellEquivalent": "Set-SPOTenant -DisableWorkflow2010 $true -DisableWorkflow2013 $true -DisableBackToClassic $true", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPDirectSharing", @@ -4360,7 +3050,8 @@ "impactColour": "warning", "addedDate": "2024-07-09", "powershellEquivalent": "Set-SPOTenant -DefaultSharingLinkType Direct", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPExternalUserExpiration", @@ -4385,7 +3076,8 @@ "impactColour": "warning", "addedDate": "2024-07-09", "powershellEquivalent": "Set-SPOTenant -ExternalUserExpireInDays 30 -ExternalUserExpirationRequired $True", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPEmailAttestation", @@ -4410,18 +3102,13 @@ "impactColour": "warning", "addedDate": "2024-07-09", "powershellEquivalent": "Set-SPOTenant -EmailAttestationRequired $true -EmailAttestationReAuthDays 15", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.DefaultSharingLink", "cat": "SharePoint Standards", - "tag": [ - "CIS M365 5.0 (7.2.7)", - "CIS M365 5.0 (7.2.11)", - "CISA (MS.SPO.1.4v1)", - "ZTNA21803", - "ZTNA21804" - ], + "tag": ["CIS M365 5.0 (7.2.7)", "CIS M365 5.0 (7.2.11)", "CISA (MS.SPO.1.4v1)", "ZTNA21803", "ZTNA21804"], "helpText": "Configure the SharePoint default sharing link type and permission. This setting controls both the type of sharing link created by default and the permission level assigned to those links.", "docsDescription": "Sets the default sharing link type (Direct or Internal) and permission (View) in SharePoint and OneDrive. Direct sharing means links only work for specific people, while Internal sharing means links work for anyone in the organization. Setting the view permission as the default ensures that users must deliberately select the edit permission when sharing a link, reducing the risk of unintentionally granting edit privileges.", "executiveText": "Configures SharePoint default sharing links to implement the principle of least privilege for document sharing. This security measure reduces the risk of accidental data modification while maintaining collaboration functionality, requiring users to explicitly select Edit permissions when necessary. The sharing type setting controls whether links are restricted to specific recipients or available to the entire organization. This reduces the risk of accidental data exposure through link sharing.", @@ -4434,14 +3121,8 @@ "label": "Default Sharing Link Type", "name": "standards.DefaultSharingLink.SharingLinkType", "options": [ - { - "label": "Direct - Only the people the user specifies", - "value": "Direct" - }, - { - "label": "Internal - Only people in your organization", - "value": "Internal" - } + { "label": "Direct - Only the people the user specifies", "value": "Direct" }, + { "label": "Internal - Only people in your organization", "value": "Internal" } ] } ], @@ -4450,7 +3131,8 @@ "impactColour": "info", "addedDate": "2025-06-13", "powershellEquivalent": "Set-SPOTenant -DefaultSharingLinkType [Direct|Internal] -DefaultLinkPermission View", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.DisableAddShortcutsToOneDrive", @@ -4466,14 +3148,8 @@ "label": "Add Shortcuts To OneDrive button state", "name": "standards.DisableAddShortcutsToOneDrive.state", "options": [ - { - "label": "Disabled", - "value": "true" - }, - { - "label": "Enabled", - "value": "false" - } + { "label": "Disabled", "value": "true" }, + { "label": "Enabled", "value": "false" } ] } ], @@ -4482,7 +3158,8 @@ "impactColour": "warning", "addedDate": "2023-07-25", "powershellEquivalent": "Set-SPOTenant -DisableAddShortcutsToOneDrive $true or $false", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.SPSyncButtonState", @@ -4498,14 +3175,8 @@ "label": "SharePoint Sync Button state", "name": "standards.SPSyncButtonState.state", "options": [ - { - "label": "Disabled", - "value": "true" - }, - { - "label": "Enabled", - "value": "false" - } + { "label": "Disabled", "value": "true" }, + { "label": "Enabled", "value": "false" } ] } ], @@ -4514,20 +3185,13 @@ "impactColour": "warning", "addedDate": "2024-07-26", "powershellEquivalent": "Set-SPOTenant -HideSyncButtonOnTeamSite $true or $false", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.DisableSharePointLegacyAuth", "cat": "SharePoint Standards", - "tag": [ - "CIS M365 5.0 (6.5.1)", - "CIS M365 5.0 (7.2.1)", - "spo_legacy_auth", - "CISA (MS.AAD.3.1v1)", - "NIST CSF 2.0 (PR.IR-01)", - "ZTNA21776", - "ZTNA21797" - ], + "tag": ["CIS M365 5.0 (6.5.1)", "CIS M365 5.0 (7.2.1)", "spo_legacy_auth", "CISA (MS.AAD.3.1v1)", "NIST CSF 2.0 (PR.IR-01)", "ZTNA21776", "ZTNA21797"], "helpText": "Disables the ability to authenticate with SharePoint using legacy authentication methods. Any applications that use legacy authentication will need to be updated to use modern authentication.", "docsDescription": "Disables the ability for users and applications to access SharePoint via legacy basic authentication. This will likely not have any user impact, but will block systems/applications depending on basic auth or the SharePointOnlineCredentials class.", "executiveText": "Disables outdated authentication methods for SharePoint access, forcing applications and users to use modern, more secure authentication protocols. This significantly improves security by eliminating vulnerable authentication pathways while requiring updates to older applications.", @@ -4537,18 +3201,13 @@ "impactColour": "warning", "addedDate": "2024-02-05", "powershellEquivalent": "Set-SPOTenant -LegacyAuthProtocolsEnabled $false", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.sharingCapability", "cat": "SharePoint Standards", - "tag": [ - "CIS M365 5.0 (7.2.3)", - "CISA (MS.AAD.14.1v1)", - "CISA (MS.SPO.1.1v1)", - "ZTNA21803", - "ZTNA21804" - ], + "tag": ["CIS M365 5.0 (7.2.3)", "CISA (MS.AAD.14.1v1)", "CISA (MS.SPO.1.1v1)", "ZTNA21803", "ZTNA21804"], "helpText": "Sets the default sharing level for OneDrive and SharePoint. This is a tenant wide setting and overrules any settings set on the site level", "executiveText": "Defines the organization's default policy for sharing files and folders in SharePoint and OneDrive, balancing collaboration needs with security requirements. This fundamental setting determines whether employees can share with external users, anonymous links, or only internal colleagues.", "addedComponent": [ @@ -4558,22 +3217,10 @@ "label": "Select Sharing Level", "name": "standards.sharingCapability.Level", "options": [ - { - "label": "Users can share only with people in the organization. No external sharing is allowed.", - "value": "disabled" - }, - { - "label": "Users can share with new and existing guests. Guests must sign in or provide a verification code.", - "value": "externalUserSharingOnly" - }, - { - "label": "Users can share with anyone by using links that do not require sign-in.", - "value": "externalUserAndGuestSharing" - }, - { - "label": "Users can share with existing guests (those already in the directory of the organization).", - "value": "existingExternalUserSharingOnly" - } + { "label": "Users can share only with people in the organization. No external sharing is allowed.", "value": "disabled" }, + { "label": "Users can share with new and existing guests. Guests must sign in or provide a verification code.", "value": "externalUserSharingOnly" }, + { "label": "Users can share with anyone by using links that do not require sign-in.", "value": "externalUserAndGuestSharing" }, + { "label": "Users can share with existing guests (those already in the directory of the organization).", "value": "existingExternalUserSharingOnly" } ] } ], @@ -4582,18 +3229,13 @@ "impactColour": "danger", "addedDate": "2022-06-15", "powershellEquivalent": "Update-MgBetaAdminSharePointSetting", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.DisableReshare", "cat": "SharePoint Standards", - "tag": [ - "CIS M365 5.0 (7.2.5)", - "CISA (MS.AAD.14.2v1)", - "CISA (MS.SPO.1.2v1)", - "ZTNA21803", - "ZTNA21804" - ], + "tag": ["CIS M365 5.0 (7.2.5)", "CISA (MS.AAD.14.2v1)", "CISA (MS.SPO.1.2v1)", "ZTNA21803", "ZTNA21804"], "helpText": "Disables the ability for external users to share files they don't own. Sharing links can only be made for People with existing access", "docsDescription": "Disables the ability for external users to share files they don't own. Sharing links can only be made for People with existing access. This is a tenant wide setting and overrules any settings set on the site level", "executiveText": "Prevents external users from sharing company documents with additional people, maintaining control over document distribution and preventing unauthorized access expansion. This security measure ensures that external sharing remains within intended boundaries set by internal employees.", @@ -4603,7 +3245,8 @@ "impactColour": "danger", "addedDate": "2022-06-15", "powershellEquivalent": "Update-MgBetaAdminSharePointSetting", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.DisableUserSiteCreate", @@ -4618,7 +3261,8 @@ "impactColour": "danger", "addedDate": "2022-06-15", "powershellEquivalent": "Update-MgAdminSharePointSetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.ExcludedfileExt", @@ -4627,18 +3271,15 @@ "helpText": "Sets the file extensions that are excluded from syncing with OneDrive. These files will be blocked from upload. '*.' is automatically added to the extension and can be omitted.", "executiveText": "Blocks specific file types from being uploaded or synchronized to OneDrive, helping prevent security risks from potentially dangerous file formats. This security measure protects against malware distribution while allowing legitimate business file types to be shared safely.", "addedComponent": [ - { - "type": "textField", - "name": "standards.ExcludedfileExt.ext", - "label": "Extensions, Comma separated" - } + { "type": "textField", "name": "standards.ExcludedfileExt.ext", "label": "Extensions, Comma separated" } ], "label": "Exclude File Extensions from Syncing", "impact": "High Impact", "impactColour": "danger", "addedDate": "2022-06-15", "powershellEquivalent": "Update-MgAdminSharePointSetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.disableMacSync", @@ -4652,7 +3293,8 @@ "impactColour": "danger", "addedDate": "2022-06-15", "powershellEquivalent": "Update-MgAdminSharePointSetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.unmanagedSync", @@ -4669,14 +3311,8 @@ "name": "standards.unmanagedSync.state", "label": "State", "options": [ - { - "label": "Allow limited, web-only access", - "value": "1" - }, - { - "label": "Block access", - "value": "2" - } + { "label": "Allow limited, web-only access", "value": "1" }, + { "label": "Block access", "value": "2" } ], "required": false } @@ -4686,18 +3322,13 @@ "impactColour": "danger", "addedDate": "2025-06-13", "powershellEquivalent": "Set-SPOTenant -ConditionalAccessPolicy AllowFullAccess | AllowLimitedAccess | BlockAccess", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.sharingDomainRestriction", "cat": "SharePoint Standards", - "tag": [ - "CIS M365 5.0 (7.2.6)", - "CISA (MS.AAD.14.3v1)", - "CISA (MS.SPO.1.3v1)", - "ZTNA21803", - "ZTNA21804" - ], + "tag": ["CIS M365 5.0 (7.2.6)", "CISA (MS.AAD.14.3v1)", "CISA (MS.SPO.1.3v1)", "ZTNA21803", "ZTNA21804"], "helpText": "Restricts sharing to only users with the specified domain. This is useful for organizations that only want to share with their own domain.", "executiveText": "Controls which external domains employees can share files with, enabling secure collaboration with trusted partners while blocking sharing with unauthorized organizations. This targeted approach maintains necessary business relationships while preventing data exposure to unknown entities.", "addedComponent": [ @@ -4707,45 +3338,25 @@ "name": "standards.sharingDomainRestriction.Mode", "label": "Limit external sharing by domains", "options": [ - { - "label": "Off", - "value": "none" - }, - { - "label": "Restrict sharing to specific domains", - "value": "allowList" - }, - { - "label": "Block sharing to specific domains", - "value": "blockList" - } + { "label": "Off", "value": "none" }, + { "label": "Restrict sharing to specific domains", "value": "allowList" }, + { "label": "Block sharing to specific domains", "value": "blockList" } ] }, - { - "type": "textField", - "name": "standards.sharingDomainRestriction.Domains", - "label": "Domains to allow/block, comma separated", - "required": false - } + { "type": "textField", "name": "standards.sharingDomainRestriction.Domains", "label": "Domains to allow/block, comma separated", "required": false } ], "label": "Restrict sharing to a specific domain", "impact": "High Impact", "impactColour": "danger", "addedDate": "2024-06-20", "powershellEquivalent": "Update-MgAdminSharePointSetting", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["SHAREPOINTWAC", "SHAREPOINTSTANDARD", "SHAREPOINTENTERPRISE", "SHAREPOINTENTERPRISE_EDU", "ONEDRIVE_BASIC", "ONEDRIVE_ENTERPRISE"] }, { "name": "standards.TeamsGlobalMeetingPolicy", "cat": "Teams Standards", - "tag": [ - "CIS M365 5.0 (8.5.1)", - "CIS M365 5.0 (8.5.2)", - "CIS M365 5.0 (8.5.3)", - "CIS M365 5.0 (8.5.4)", - "CIS M365 5.0 (8.5.5)", - "CIS M365 5.0 (8.5.6)" - ], + "tag": ["CIS M365 5.0 (8.5.1)", "CIS M365 5.0 (8.5.2)", "CIS M365 5.0 (8.5.3)", "CIS M365 5.0 (8.5.4)", "CIS M365 5.0 (8.5.5)", "CIS M365 5.0 (8.5.6)"], "helpText": "Defines the CIS recommended global meeting policy for Teams. This includes AllowAnonymousUsersToJoinMeeting, AllowAnonymousUsersToStartMeeting, AutoAdmittedUsers, AllowPSTNUsersToBypassLobby, MeetingChatEnabledType, DesignatedPresenterRoleMode, AllowExternalParticipantGiveRequestControl, AllowParticipantGiveRequestControl", "executiveText": "Establishes security-focused default settings for Teams meetings, controlling who can join meetings, present content, and participate in chats. These policies balance collaboration needs with security requirements, ensuring meetings remain productive while protecting against unauthorized access and disruption.", "addedComponent": [ @@ -4757,34 +3368,14 @@ "name": "standards.TeamsGlobalMeetingPolicy.DesignatedPresenterRoleMode", "label": "Default value of the `Who can present?`", "options": [ - { - "label": "Everyone", - "value": "EveryoneUserOverride" - }, - { - "label": "People in my organization", - "value": "EveryoneInCompanyUserOverride" - }, - { - "label": "People in my organization and trusted organizations", - "value": "EveryoneInSameAndFederatedCompanyUserOverride" - }, - { - "label": "Only organizer", - "value": "OrganizerOnlyUserOverride" - } + { "label": "Everyone", "value": "EveryoneUserOverride" }, + { "label": "People in my organization", "value": "EveryoneInCompanyUserOverride" }, + { "label": "People in my organization and trusted organizations", "value": "EveryoneInSameAndFederatedCompanyUserOverride" }, + { "label": "Only organizer", "value": "OrganizerOnlyUserOverride" } ] }, - { - "type": "switch", - "name": "standards.TeamsGlobalMeetingPolicy.AllowAnonymousUsersToJoinMeeting", - "label": "Allow anonymous users to join meeting" - }, - { - "type": "switch", - "name": "standards.TeamsGlobalMeetingPolicy.AllowAnonymousUsersToStartMeeting", - "label": "Allow anonymous users to start meeting" - }, + { "type": "switch", "name": "standards.TeamsGlobalMeetingPolicy.AllowAnonymousUsersToJoinMeeting", "label": "Allow anonymous users to join meeting" }, + { "type": "switch", "name": "standards.TeamsGlobalMeetingPolicy.AllowAnonymousUsersToStartMeeting", "label": "Allow anonymous users to start meeting" }, { "type": "autoComplete", "required": false, @@ -4794,33 +3385,14 @@ "label": "Who can bypass the lobby?", "helperText": "If left blank, the current value will not be changed.", "options": [ - { - "label": "Only organizers and co-organizers", - "value": "OrganizerOnly" - }, - { - "label": "People in organization excluding guests", - "value": "EveryoneInCompanyExcludingGuests" - }, - { - "label": "People in same or federated organizations", - "value": "EveryoneInSameAndFederatedCompany" - }, - { - "label": "People who were invited", - "value": "InvitedUsers" - }, - { - "label": "Everyone", - "value": "Everyone" - } + { "label": "Only organizers and co-organizers", "value": "OrganizerOnly" }, + { "label": "People in organization excluding guests", "value": "EveryoneInCompanyExcludingGuests" }, + { "label": "People in same or federated organizations", "value": "EveryoneInSameAndFederatedCompany" }, + { "label": "People who were invited", "value": "InvitedUsers" }, + { "label": "Everyone", "value": "Everyone" } ] }, - { - "type": "switch", - "name": "standards.TeamsGlobalMeetingPolicy.AllowPSTNUsersToBypassLobby", - "label": "Allow dial-in users to bypass lobby" - }, + { "type": "switch", "name": "standards.TeamsGlobalMeetingPolicy.AllowPSTNUsersToBypassLobby", "label": "Allow dial-in users to bypass lobby" }, { "type": "autoComplete", "required": true, @@ -4829,37 +3401,21 @@ "name": "standards.TeamsGlobalMeetingPolicy.MeetingChatEnabledType", "label": "Meeting chat policy", "options": [ - { - "label": "On for everyone", - "value": "Enabled" - }, - { - "label": "On for everyone but anonymous users", - "value": "EnabledExceptAnonymous" - }, - { - "label": "Off for everyone", - "value": "Disabled" - } + { "label": "On for everyone", "value": "Enabled" }, + { "label": "On for everyone but anonymous users", "value": "EnabledExceptAnonymous" }, + { "label": "Off for everyone", "value": "Disabled" } ] }, - { - "type": "switch", - "name": "standards.TeamsGlobalMeetingPolicy.AllowParticipantGiveRequestControl", - "label": "Participants can give or request control" - }, - { - "type": "switch", - "name": "standards.TeamsGlobalMeetingPolicy.AllowExternalParticipantGiveRequestControl", - "label": "External participants can give or request control" - } + { "type": "switch", "name": "standards.TeamsGlobalMeetingPolicy.AllowParticipantGiveRequestControl", "label": "Participants can give or request control" }, + { "type": "switch", "name": "standards.TeamsGlobalMeetingPolicy.AllowExternalParticipantGiveRequestControl", "label": "External participants can give or request control" } ], "label": "Define Global Meeting Policy for Teams", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-11-12", "powershellEquivalent": "Set-CsTeamsMeetingPolicy -AllowAnonymousUsersToJoinMeeting $false -AllowAnonymousUsersToStartMeeting $false -AutoAdmittedUsers $AutoAdmittedUsers -AllowPSTNUsersToBypassLobby $false -MeetingChatEnabledType EnabledExceptAnonymous -DesignatedPresenterRoleMode $DesignatedPresenterRoleMode -AllowExternalParticipantGiveRequestControl $false -AllowParticipantGiveRequestControl $false", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsChatProtection", @@ -4869,25 +3425,16 @@ "docsDescription": "Configures Teams messaging safety features to protect users from weaponizable files and malicious URLs in chats and channels. Weaponizable File Protection automatically blocks messages containing potentially dangerous file types (like .exe, .dll, .bat, etc.). Malicious URL Protection scans URLs in messages and displays warnings when potentially harmful links are detected. These protections work across internal and external collaboration scenarios.", "executiveText": "Enables automated security protections in Microsoft Teams to block dangerous files and warn users about malicious links in chat messages. This helps protect employees from file-based attacks and phishing attempts. These safeguards work seamlessly in the background, providing essential protection without disrupting normal business communication.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsChatProtection.FileTypeCheck", - "label": "Enable Weaponizable File Protection", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.TeamsChatProtection.UrlReputationCheck", - "label": "Enable Malicious URL Protection", - "defaultValue": true - } + { "type": "switch", "name": "standards.TeamsChatProtection.FileTypeCheck", "label": "Enable Weaponizable File Protection", "defaultValue": true }, + { "type": "switch", "name": "standards.TeamsChatProtection.UrlReputationCheck", "label": "Enable Malicious URL Protection", "defaultValue": true } ], "label": "Set Teams Chat Protection Settings", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-10-02", "powershellEquivalent": "Set-CsTeamsMessagingConfiguration -FileTypeCheck 'Enabled' -UrlReputationCheck 'Enabled' -ReportIncorrectSecurityDetections 'Enabled'", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsExternalChatWithAnyone", @@ -4902,14 +3449,8 @@ "name": "standards.TeamsExternalChatWithAnyone.UseB2BInvitesToAddExternalUsers", "label": "Allow chatting with anyone via email", "options": [ - { - "label": "Enabled", - "value": "true" - }, - { - "label": "Disabled", - "value": "false" - } + { "label": "Enabled", "value": "true" }, + { "label": "Disabled", "value": "false" } ], "defaultValue": "Disabled" } @@ -4919,7 +3460,8 @@ "impactColour": "info", "addedDate": "2025-11-03", "powershellEquivalent": "Set-CsTeamsMessagingPolicy -Identity Global -UseB2BInvitesToAddExternalUsers $false/$true", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsEmailIntegration", @@ -4928,11 +3470,7 @@ "docsDescription": "Teams channel email addresses are an optional feature that allows users to email the Teams channel directly.", "executiveText": "Controls whether Teams channels can receive emails directly, enabling integration between email and team collaboration. This feature can improve workflow efficiency by allowing external communications to flow into team discussions, though it may need management for security or organizational reasons.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsEmailIntegration.AllowEmailIntoChannel", - "label": "Allow channel emails" - } + { "type": "switch", "name": "standards.TeamsEmailIntegration.AllowEmailIntoChannel", "label": "Allow channel emails" } ], "label": "Disallow emails to be sent to channel email addresses", "impact": "Low Impact", @@ -4940,7 +3478,8 @@ "addedDate": "2024-07-30", "powershellEquivalent": "Set-CsTeamsClientConfiguration -AllowEmailIntoChannel $false", "recommendedBy": ["CIS"], - "tag": ["CIS M365 5.0 (8.1.2)"] + "tag": ["CIS M365 5.0 (8.1.2)"], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsGuestAccess", @@ -4950,18 +3489,15 @@ "docsDescription": "Allow guest users access to teams. Guest users are users who are not part of your organization but have been invited to collaborate with your organization in Teams. This setting allows you to control whether guest users can access Teams.", "executiveText": "Determines whether external partners, vendors, and collaborators can be invited to participate in Teams conversations and meetings. This fundamental setting enables external collaboration while requiring careful management to balance openness with security and information protection.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsGuestAccess.AllowGuestUser", - "label": "Allow guest users" - } + { "type": "switch", "name": "standards.TeamsGuestAccess.AllowGuestUser", "label": "Allow guest users" } ], "label": "Allow guest users in Teams", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-06-03", "powershellEquivalent": "Set-CsTeamsClientConfiguration -AllowGuestUser $true", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsMeetingVerification", @@ -4978,14 +3514,8 @@ "label": "CAPTCHA Verification Setting", "name": "standards.TeamsMeetingVerification.CaptchaVerificationForMeetingJoin", "options": [ - { - "label": "Not Required", - "value": "NotRequired" - }, - { - "label": "Anonymous Users and Untrusted Organizations", - "value": "AnonymousUsersAndUntrustedOrganizations" - } + { "label": "Not Required", "value": "NotRequired" }, + { "label": "Anonymous Users and Untrusted Organizations", "value": "AnonymousUsersAndUntrustedOrganizations" } ] } ], @@ -4994,7 +3524,8 @@ "impactColour": "info", "addedDate": "2025-06-14", "powershellEquivalent": "Set-CsTeamsMeetingPolicy -CaptchaVerificationForMeetingJoin", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsExternalFileSharing", @@ -5003,38 +3534,19 @@ "helpText": "Ensure external file sharing in Teams is enabled for only approved cloud storage services.", "executiveText": "Controls which external cloud storage services (like Google Drive, Dropbox, Box) employees can access through Teams, ensuring file sharing occurs only through approved and secure platforms. This helps maintain data governance while supporting necessary business integrations.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsExternalFileSharing.AllowGoogleDrive", - "label": "Allow Google Drive" - }, - { - "type": "switch", - "name": "standards.TeamsExternalFileSharing.AllowShareFile", - "label": "Allow ShareFile" - }, - { - "type": "switch", - "name": "standards.TeamsExternalFileSharing.AllowBox", - "label": "Allow Box" - }, - { - "type": "switch", - "name": "standards.TeamsExternalFileSharing.AllowDropBox", - "label": "Allow Dropbox" - }, - { - "type": "switch", - "name": "standards.TeamsExternalFileSharing.AllowEgnyte", - "label": "Allow Egnyte" - } + { "type": "switch", "name": "standards.TeamsExternalFileSharing.AllowGoogleDrive", "label": "Allow Google Drive" }, + { "type": "switch", "name": "standards.TeamsExternalFileSharing.AllowShareFile", "label": "Allow ShareFile" }, + { "type": "switch", "name": "standards.TeamsExternalFileSharing.AllowBox", "label": "Allow Box" }, + { "type": "switch", "name": "standards.TeamsExternalFileSharing.AllowDropBox", "label": "Allow Dropbox" }, + { "type": "switch", "name": "standards.TeamsExternalFileSharing.AllowEgnyte", "label": "Allow Egnyte" } ], "label": "Define approved cloud storage services for external file sharing in Teams", "impact": "Low Impact", "impactColour": "info", "addedDate": "2024-07-28", "powershellEquivalent": "Set-CsTeamsClientConfiguration -AllowGoogleDrive $false -AllowShareFile $false -AllowBox $false -AllowDropBox $false -AllowEgnyte $false", - "recommendedBy": ["CIS"] + "recommendedBy": ["CIS"], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsEnrollUser", @@ -5052,14 +3564,8 @@ "name": "standards.TeamsEnrollUser.EnrollUserOverride", "label": "Voice and Face Enrollment", "options": [ - { - "label": "Disabled", - "value": "Disabled" - }, - { - "label": "Enabled", - "value": "Enabled" - } + { "label": "Disabled", "value": "Disabled" }, + { "label": "Enabled", "value": "Enabled" } ] } ], @@ -5068,7 +3574,8 @@ "impactColour": "info", "addedDate": "2024-11-12", "powershellEquivalent": "Set-CsTeamsMeetingPolicy -Identity Global -EnrollUserOverride $false", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsExternalAccessPolicy", @@ -5078,23 +3585,16 @@ "docsDescription": "Sets the properties of the Global external access policy. External access policies determine whether or not your users can: 1) communicate with users who have Session Initiation Protocol (SIP) accounts with a federated organization; 2) communicate with users who are using custom applications built with Azure Communication Services; 3) access Skype for Business Server over the Internet, without having to log on to your internal network; 4) communicate with users who have SIP accounts with a public instant messaging (IM) provider such as Skype; and, 5) communicate with people who are using Teams with an account that's not managed by an organization.", "executiveText": "Defines the organization's policy for communicating with external users through Teams, including other organizations, Skype users, and unmanaged accounts. This fundamental setting determines the scope of external collaboration while maintaining security boundaries for business communications.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsExternalAccessPolicy.EnableFederationAccess", - "label": "Allow communication from trusted organizations" - }, - { - "type": "switch", - "name": "standards.TeamsExternalAccessPolicy.EnableTeamsConsumerAccess", - "label": "Allow communication with unmanaged Teams accounts" - } + { "type": "switch", "name": "standards.TeamsExternalAccessPolicy.EnableFederationAccess", "label": "Allow communication from trusted organizations" }, + { "type": "switch", "name": "standards.TeamsExternalAccessPolicy.EnableTeamsConsumerAccess", "label": "Allow communication with unmanaged Teams accounts" } ], "label": "External Access Settings for Microsoft Teams", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2024-07-30", "powershellEquivalent": "Set-CsExternalAccessPolicy", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsFederationConfiguration", @@ -5104,11 +3604,7 @@ "docsDescription": "Sets the properties of the Global federation configuration. Federation configuration settings determine whether or not your users can communicate with users who have SIP accounts with a federated organization.", "executiveText": "Configures how the organization federates with external organizations for Teams communication, controlling whether employees can communicate with specific external domains or all external organizations. This setting enables secure inter-organizational collaboration while maintaining control over external communications.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsFederationConfiguration.AllowTeamsConsumer", - "label": "Allow users to communicate with other organizations" - }, + { "type": "switch", "name": "standards.TeamsFederationConfiguration.AllowTeamsConsumer", "label": "Allow users to communicate with other organizations" }, { "type": "autoComplete", "required": true, @@ -5117,22 +3613,10 @@ "name": "standards.TeamsFederationConfiguration.DomainControl", "label": "Communication Mode", "options": [ - { - "label": "Allow all external domains", - "value": "AllowAllExternal" - }, - { - "label": "Block all external domains", - "value": "BlockAllExternal" - }, - { - "label": "Allow specific external domains", - "value": "AllowSpecificExternal" - }, - { - "label": "Block specific external domains", - "value": "BlockSpecificExternal" - } + { "label": "Allow all external domains", "value": "AllowAllExternal" }, + { "label": "Block all external domains", "value": "BlockAllExternal" }, + { "label": "Allow specific external domains", "value": "AllowSpecificExternal" }, + { "label": "Block specific external domains", "value": "BlockSpecificExternal" } ] }, { @@ -5152,7 +3636,8 @@ "impactColour": "warning", "addedDate": "2024-07-31", "powershellEquivalent": "Set-CsTenantFederationConfiguration", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsMeetingRecordingExpiration", @@ -5179,7 +3664,8 @@ "impactColour": "warning", "addedDate": "2025-04-17", "powershellEquivalent": "Set-CsTeamsMeetingPolicy -Identity Global -MeetingRecordingExpirationDays ", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.TeamsMessagingPolicy", @@ -5189,30 +3675,10 @@ "docsDescription": "Sets the properties of the Global messaging policy. Messaging policies control which chat and channel messaging features are available to users in Teams.", "executiveText": "Defines what messaging capabilities employees have in Teams, including the ability to edit or delete messages, create custom emojis, and report inappropriate content. These policies help maintain professional communication standards while enabling necessary collaboration features.", "addedComponent": [ - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.AllowOwnerDeleteMessage", - "label": "Allow Owner to Delete Messages", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.AllowUserDeleteMessage", - "label": "Allow User to Delete Messages", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.AllowUserEditMessage", - "label": "Allow User to Edit Messages", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.AllowUserDeleteChat", - "label": "Allow User to Delete Chats", - "defaultValue": true - }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.AllowOwnerDeleteMessage", "label": "Allow Owner to Delete Messages", "defaultValue": false }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.AllowUserDeleteMessage", "label": "Allow User to Delete Messages", "defaultValue": true }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.AllowUserEditMessage", "label": "Allow User to Edit Messages", "defaultValue": true }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.AllowUserDeleteChat", "label": "Allow User to Delete Chats", "defaultValue": true }, { "type": "autoComplete", "required": true, @@ -5221,61 +3687,29 @@ "name": "standards.TeamsMessagingPolicy.ReadReceiptsEnabledType", "label": "Read Receipts Enabled Type", "options": [ - { - "label": "User controlled", - "value": "UserPreference" - }, - { - "label": "Turned on for everyone", - "value": "Everyone" - }, - { - "label": "Turned off for everyone", - "value": "None" - } + { "label": "User controlled", "value": "UserPreference" }, + { "label": "Turned on for everyone", "value": "Everyone" }, + { "label": "Turned off for everyone", "value": "None" } ] }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.CreateCustomEmojis", - "label": "Allow Creating Custom Emojis", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.DeleteCustomEmojis", - "label": "Allow Deleting Custom Emojis", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.AllowSecurityEndUserReporting", - "label": "Allow reporting message as security concern", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.TeamsMessagingPolicy.AllowCommunicationComplianceEndUserReporting", - "label": "Allow reporting message as inappropriate content", - "defaultValue": true - } + { "type": "switch", "name": "standards.TeamsMessagingPolicy.CreateCustomEmojis", "label": "Allow Creating Custom Emojis", "defaultValue": true }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.DeleteCustomEmojis", "label": "Allow Deleting Custom Emojis", "defaultValue": false }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.AllowSecurityEndUserReporting", "label": "Allow reporting message as security concern", "defaultValue": true }, + { "type": "switch", "name": "standards.TeamsMessagingPolicy.AllowCommunicationComplianceEndUserReporting", "label": "Allow reporting message as inappropriate content", "defaultValue": true } ], "label": "Global Messaging Policy for Microsoft Teams", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2025-01-10", "powershellEquivalent": "Set-CsTeamsMessagingPolicy", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["MCOSTANDARD", "MCOEV", "MCOIMP", "TEAMS1", "Teams_Room_Standard"] }, { "name": "standards.AutopilotStatusPage", "cat": "Device Management Standards", "tag": [], - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "helpText": "Deploy the Autopilot Status Page, which shows progress during device setup through Autopilot.", "docsDescription": "This standard allows configuration of the Autopilot Status Page, providing users with a visual representation of the progress during device setup. It includes options like timeout, logging, and retry settings.", "executiveText": "Provides employees with a visual progress indicator during automated device setup, improving the user experience when receiving new computers. This reduces IT support calls and helps ensure successful device deployment by guiding users through the setup process.", @@ -5290,89 +3724,33 @@ "max": { "value": 1440, "message": "Maximum value is 1440" } } }, - { - "type": "textField", - "name": "standards.AutopilotStatusPage.ErrorMessage", - "label": "Custom Error Message", - "required": false - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.ShowProgress", - "label": "Show progress to users", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.EnableLog", - "label": "Turn on log collection", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.OBEEOnly", - "label": "Show status page only with OOBE setup", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.InstallWindowsUpdates", - "label": "Install Windows Updates during setup", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.BlockDevice", - "label": "Block device usage during setup", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.AllowReset", - "label": "Allow reset", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotStatusPage.AllowFail", - "label": "Allow users to use device if setup fails", - "defaultValue": true - } + { "type": "textField", "name": "standards.AutopilotStatusPage.ErrorMessage", "label": "Custom Error Message", "required": false }, + { "type": "switch", "name": "standards.AutopilotStatusPage.ShowProgress", "label": "Show progress to users", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotStatusPage.EnableLog", "label": "Turn on log collection", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotStatusPage.OBEEOnly", "label": "Show status page only with OOBE setup", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotStatusPage.InstallWindowsUpdates", "label": "Install Windows Updates during setup", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotStatusPage.BlockDevice", "label": "Block device usage during setup", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotStatusPage.AllowReset", "label": "Allow reset", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotStatusPage.AllowFail", "label": "Allow users to use device if setup fails", "defaultValue": true } ], "label": "Enable Autopilot Status Page", "impact": "Low Impact", "addedDate": "2023-12-30", "impactColour": "info", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.AutopilotProfile", "cat": "Device Management Standards", "tag": [], - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "helpText": "Assign the appropriate Autopilot profile to streamline device deployment.", "docsDescription": "This standard allows the deployment of Autopilot profiles to devices, including settings such as unique name templates, language options, and local admin privileges.", "addedComponent": [ - { - "type": "textField", - "name": "standards.AutopilotProfile.DisplayName", - "label": "Profile Display Name" - }, - { - "type": "textField", - "name": "standards.AutopilotProfile.Description", - "label": "Profile Description" - }, - { - "type": "textField", - "name": "standards.AutopilotProfile.DeviceNameTemplate", - "label": "Unique Device Name Template", - "required": false - }, + { "type": "textField", "name": "standards.AutopilotProfile.DisplayName", "label": "Profile Display Name" }, + { "type": "textField", "name": "standards.AutopilotProfile.Description", "label": "Profile Description" }, + { "type": "textField", "name": "standards.AutopilotProfile.DeviceNameTemplate", "label": "Unique Device Name Template", "required": false }, { "type": "autoComplete", "multiple": false, @@ -5380,83 +3758,31 @@ "required": false, "name": "standards.AutopilotProfile.Languages", "label": "Languages", - "api": { - "url": "/languageList.json", - "labelField": "languageTag", - "valueField": "tag" - } - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.CollectHash", - "label": "Convert all targeted devices to Autopilot", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.AssignToAllDevices", - "label": "Assign to all devices", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.SelfDeployingMode", - "label": "Enable Self-deploying Mode", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.HideTerms", - "label": "Hide Terms and Conditions", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.HidePrivacy", - "label": "Hide Privacy Settings", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.HideChangeAccount", - "label": "Hide Change Account Options", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.NotLocalAdmin", - "label": "Setup user as a standard user (not local admin)", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.AllowWhiteGlove", - "label": "Allow White Glove OOBE", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.AutopilotProfile.AutoKeyboard", - "label": "Automatically configure keyboard", - "defaultValue": true - } + "api": { "url": "/languageList.json", "labelField": "languageTag", "valueField": "tag" } + }, + { "type": "switch", "name": "standards.AutopilotProfile.CollectHash", "label": "Convert all targeted devices to Autopilot", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.AssignToAllDevices", "label": "Assign to all devices", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.SelfDeployingMode", "label": "Enable Self-deploying Mode", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.HideTerms", "label": "Hide Terms and Conditions", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.HidePrivacy", "label": "Hide Privacy Settings", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.HideChangeAccount", "label": "Hide Change Account Options", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.NotLocalAdmin", "label": "Setup user as a standard user (not local admin)", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.AllowWhiteGlove", "label": "Allow White Glove OOBE", "defaultValue": true }, + { "type": "switch", "name": "standards.AutopilotProfile.AutoKeyboard", "label": "Automatically configure keyboard", "defaultValue": true } ], "label": "Enable Autopilot Profile", "impact": "Low Impact", "impactColour": "info", "addedDate": "2023-12-30", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.IntuneTemplate", "cat": "Templates", "label": "Intune Template", "multiple": true, - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "impact": "High Impact", "addedDate": "2023-12-30", "helpText": "Deploy and manage Intune templates across devices.", @@ -5475,11 +3801,7 @@ "labelField": "Displayname", "valueField": "GUID", "showRefresh": true, - "templateView": { - "title": "Intune Template", - "property": "RAWJson", - "type": "intune" - } + "templateView": { "title": "Intune Template", "property": "RAWJson", "type": "intune" } } }, { @@ -5489,65 +3811,24 @@ "creatable": false, "name": "TemplateList-Tags", "label": "Or select a package of Intune Templates", - "api": { - "queryKey": "ListIntuneTemplates-tag-autcomplete", - "url": "/api/ListIntuneTemplates?mode=Tag", - "labelField": "label", - "valueField": "value" - } + "api": { "queryKey": "ListIntuneTemplates-tag-autcomplete", "url": "/api/ListIntuneTemplates?mode=Tag", "labelField": "label", "valueField": "value" } }, { "name": "AssignTo", "label": "Who should this template be assigned to?", "type": "radio", "options": [ - { - "label": "Do not assign", - "value": "On" - }, - { - "label": "Assign to all users", - "value": "allLicensedUsers" - }, - { - "label": "Assign to all devices", - "value": "AllDevices" - }, - { - "label": "Assign to all users and devices", - "value": "AllDevicesAndUsers" - }, - { - "label": "Assign to Custom Group", - "value": "customGroup" - } + { "label": "Do not assign", "value": "On" }, + { "label": "Assign to all users", "value": "allLicensedUsers" }, + { "label": "Assign to all devices", "value": "AllDevices" }, + { "label": "Assign to all users and devices", "value": "AllDevicesAndUsers" }, + { "label": "Assign to Custom Group", "value": "customGroup" } ] }, - { - "type": "textField", - "required": false, - "name": "customGroup", - "label": "Enter the custom group name if you selected 'Assign to Custom Group'. Wildcards are allowed." - }, - { - "type": "switch", - "name": "verifyAssignments", - "label": "Verify policy assignments" - }, - { - "name": "excludeGroup", - "label": "Exclude Groups", - "type": "textField", - "required": false, - "helpText": "Enter the group name(s) to exclude from the assignment. Wildcards are allowed. Multiple group names are comma-seperated." - }, - { - "type": "textField", - "required": false, - "name": "assignmentFilter", - "label": "Assignment Filter Name (Optional)", - "helpText": "Enter the assignment filter name to apply to this policy assignment. Wildcards are allowed." - }, + { "type": "textField", "required": false, "name": "customGroup", "label": "Enter the custom group name if you selected 'Assign to Custom Group'. Wildcards are allowed." }, + { "type": "switch", "name": "verifyAssignments", "label": "Verify policy assignments" }, + { "name": "excludeGroup", "label": "Exclude Groups", "type": "textField", "required": false, "helpText": "Enter the group name(s) to exclude from the assignment. Wildcards are allowed. Multiple group names are comma-seperated." }, + { "type": "textField", "required": false, "name": "assignmentFilter", "label": "Assignment Filter Name (Optional)", "helpText": "Enter the assignment filter name to apply to this policy assignment. Wildcards are allowed." }, { "name": "assignmentFilterType", "label": "Assignment Filter Mode (Optional)", @@ -5555,14 +3836,8 @@ "required": false, "helpText": "Choose whether to include or exclude devices matching the filter. Only applies if you specified a filter name above. Defaults to Include if not specified.", "options": [ - { - "label": "Include - Assign to devices matching the filter", - "value": "include" - }, - { - "label": "Exclude - Assign to devices NOT matching the filter", - "value": "exclude" - } + { "label": "Include - Assign to devices matching the filter", "value": "include" }, + { "label": "Exclude - Assign to devices NOT matching the filter", "value": "exclude" } ] } ] @@ -5572,11 +3847,7 @@ "cat": "Templates", "label": "Reusable Settings Template", "multiple": true, - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "impact": "High Impact", "impactColour": "info", "addedDate": "2026-01-02", @@ -5596,11 +3867,7 @@ "labelField": "displayName", "valueField": "GUID", "showRefresh": true, - "templateView": { - "title": "Reusable Settings", - "property": "RawJSON", - "type": "intune" - } + "templateView": { "title": "Reusable Settings", "property": "RawJSON", "type": "intune" } } } ], @@ -5611,11 +3878,7 @@ "name": "standards.TransportRuleTemplate", "label": "Transport Rule Template", "cat": "Templates", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "impact": "Medium Impact", "addedDate": "2023-12-30", "helpText": "Deploy transport rules to manage email flow.", @@ -5625,31 +3888,18 @@ "type": "autoComplete", "name": "transportRuleTemplate", "label": "Select Transport Rule Template", - "api": { - "url": "/api/ListTransportRulesTemplates?noJson=true", - "labelField": "name", - "valueField": "GUID", - "queryKey": "ListTransportRulesTemplates" - } + "api": { "url": "/api/ListTransportRulesTemplates?noJson=true", "labelField": "name", "valueField": "GUID", "queryKey": "ListTransportRulesTemplates" } }, - { - "type": "switch", - "label": "Overwrite existing transport rules", - "name": "overwrite", - "defaultValue": true - } - ] + { "type": "switch", "label": "Overwrite existing transport rules", "name": "overwrite", "defaultValue": true } + ], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.ConditionalAccessTemplate", "label": "Conditional Access Template", "cat": "Templates", "multiple": true, - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "impact": "High Impact", "addedDate": "2023-12-30", "helpText": "Manage conditional access policies for better security.", @@ -5666,9 +3916,7 @@ "valueField": "GUID", "queryKey": "ListCATemplates", "showRefresh": true, - "templateView": { - "title": "Conditional Access Policy" - } + "templateView": { "title": "Conditional Access Policy" } } }, { @@ -5676,45 +3924,22 @@ "label": "What state should we deploy this template in?", "type": "radio", "options": [ - { - "value": "donotchange", - "label": "Do not change state" - }, - { - "value": "Enabled", - "label": "Set to enabled" - }, - { - "value": "Disabled", - "label": "Set to disabled" - }, - { - "value": "enabledForReportingButNotEnforced", - "label": "Set to report only" - } + { "value": "donotchange", "label": "Do not change state" }, + { "value": "Enabled", "label": "Set to enabled" }, + { "value": "Disabled", "label": "Set to disabled" }, + { "value": "enabledForReportingButNotEnforced", "label": "Set to report only" } ] }, - { - "type": "switch", - "name": "DisableSD", - "label": "Disable Security Defaults when deploying policy" - }, - { - "type": "switch", - "name": "CreateGroups", - "label": "Create groups if they do not exist" - } - ] + { "type": "switch", "name": "DisableSD", "label": "Disable Security Defaults when deploying policy" }, + { "type": "switch", "name": "CreateGroups", "label": "Create groups if they do not exist" } + ], + "requiredCapabilities": ["AAD_PREMIUM", "AAD_PREMIUM_P2"] }, { "name": "standards.ExchangeConnectorTemplate", "label": "Exchange Connector Template", "cat": "Templates", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "impact": "Medium Impact", "addedDate": "2023-12-30", "helpText": "Deploy and manage Exchange connectors.", @@ -5724,25 +3949,17 @@ "type": "autoComplete", "name": "exConnectorTemplate", "label": "Select Exchange Connector Template", - "api": { - "url": "/api/ListExConnectorTemplates", - "labelField": "name", - "valueField": "GUID", - "queryKey": "ListExConnectorTemplates" - } + "api": { "url": "/api/ListExConnectorTemplates", "labelField": "name", "valueField": "GUID", "queryKey": "ListExConnectorTemplates" } } - ] + ], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.GroupTemplate", "label": "Group Template", "multi": true, "cat": "Templates", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "impact": "Medium Impact", "addedDate": "2023-12-30", "helpText": "Deploy and manage group templates.", @@ -5752,26 +3969,17 @@ "type": "autoComplete", "name": "groupTemplate", "label": "Select Group Template", - "api": { - "url": "/api/ListGroupTemplates", - "labelField": "Displayname", - "altLabelField": "displayName", - "valueField": "GUID", - "queryKey": "ListGroupTemplates" - } + "api": { "url": "/api/ListGroupTemplates", "labelField": "Displayname", "altLabelField": "displayName", "valueField": "GUID", "queryKey": "ListGroupTemplates" } } - ] + ], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_LITE"] }, { "name": "standards.AssignmentFilterTemplate", "label": "Assignment Filter Template", "multi": true, "cat": "Templates", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, + "disabledFeatures": { "report": true, "warn": true, "remediate": false }, "impact": "Medium Impact", "addedDate": "2025-10-04", "helpText": "Deploy and manage assignment filter templates.", @@ -5781,25 +3989,16 @@ "type": "autoComplete", "name": "assignmentFilterTemplate", "label": "Select Assignment Filter Template", - "api": { - "url": "/api/ListAssignmentFilterTemplates", - "labelField": "Displayname", - "altLabelField": "displayName", - "valueField": "GUID", - "queryKey": "ListAssignmentFilterTemplates" - } + "api": { "url": "/api/ListAssignmentFilterTemplates", "labelField": "Displayname", "altLabelField": "displayName", "valueField": "GUID", "queryKey": "ListAssignmentFilterTemplates" } } - ] + ], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.TenantAllowBlockListTemplate", "label": "Tenant Allow/Block List Template", "cat": "Exchange Standards", - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, + "disabledFeatures": { "report": false, "warn": false, "remediate": false }, "impact": "Medium Impact", "addedDate": "2026-04-02", "helpText": "Deploy tenant allow/block list entries from a saved template.", @@ -5811,15 +4010,10 @@ "required": false, "multiple": true, "label": "Select Tenant Allow/Block List Template", - "api": { - "url": "/api/ListTenantAllowBlockListTemplates", - "labelField": "templateName", - "valueField": "GUID", - "queryKey": "ListTenantAllowBlockListTemplates", - "showRefresh": true - } + "api": { "url": "/api/ListTenantAllowBlockListTemplates", "labelField": "templateName", "valueField": "GUID", "queryKey": "ListTenantAllowBlockListTemplates", "showRefresh": true } } - ] + ], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.MailboxRecipientLimits", @@ -5845,7 +4039,8 @@ "impactColour": "info", "addedDate": "2025-05-28", "powershellEquivalent": "Set-Mailbox -RecipientLimits", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DisableExchangeOnlinePowerShell", @@ -5859,7 +4054,8 @@ "impactColour": "warning", "addedDate": "2025-06-19", "powershellEquivalent": "Set-User -Identity $user -RemotePowerShellEnabled $false", - "recommendedBy": ["CIS", "CIPP"] + "recommendedBy": ["CIS", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.OWAAttachmentRestrictions", @@ -5874,14 +4070,8 @@ "name": "standards.OWAAttachmentRestrictions.ConditionalAccessPolicy", "label": "Attachment Restriction Policy", "options": [ - { - "label": "Read Only (View/Edit via Office Online, no download)", - "value": "ReadOnly" - }, - { - "label": "Read Only Plus Attachments Blocked (Cannot see attachments)", - "value": "ReadOnlyPlusAttachmentsBlocked" - } + { "label": "Read Only (View/Edit via Office Online, no download)", "value": "ReadOnly" }, + { "label": "Read Only Plus Attachments Blocked (Cannot see attachments)", "value": "ReadOnlyPlusAttachmentsBlocked" } ], "defaultValue": "ReadOnlyPlusAttachmentsBlocked" } @@ -5891,7 +4081,8 @@ "impactColour": "warning", "addedDate": "2025-08-22", "powershellEquivalent": "Set-OwaMailboxPolicy -Identity \"OwaMailboxPolicy-Default\" -ConditionalAccessPolicy ReadOnlyPlusAttachmentsBlocked", - "recommendedBy": ["Microsoft Zero Trust", "CIPP"] + "recommendedBy": ["Microsoft Zero Trust", "CIPP"], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.LegacyEmailReportAddins", @@ -5915,198 +4106,49 @@ "docsDescription": "Creates an Intune Win32 script application that writes registry keys to install and configure the Check by CyberDrain browser extension on managed devices for both Google Chrome and Microsoft Edge browsers. Uses a PowerShell detection script to enforce configuration drift — when settings change in CIPP the app is automatically redeployed.", "executiveText": "Automatically deploys the Check by CyberDrain browser extension across all company devices with configurable security and branding settings, ensuring consistent security monitoring and compliance capabilities. This extension provides enhanced security features and monitoring tools that help protect against threats while maintaining user productivity.", "addedComponent": [ - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.showNotifications", - "label": "Show notifications", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.enableValidPageBadge", - "label": "Enable valid page badge", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.enablePageBlocking", - "label": "Enable page blocking", - "defaultValue": true - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.forceToolbarPin", - "label": "Force pin extension to toolbar", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.enableCippReporting", - "label": "Enable CIPP reporting", - "defaultValue": true - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.customRulesUrl", - "label": "Custom Rules URL", - "placeholder": "https://YOUR-CIPP-SERVER-URL/rules.json", - "helperText": "Enter the URL for custom rules if you have them. This should point to a JSON file with the same structure as the rules.json used for CIPP reporting.", - "required": false - }, - { - "type": "number", - "name": "standards.DeployCheckChromeExtension.updateInterval", - "label": "Update interval (hours)", - "defaultValue": 24 - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.enableDebugLogging", - "label": "Enable debug logging", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.enableGenericWebhook", - "label": "Enable generic webhook", - "defaultValue": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.webhookUrl", - "label": "Webhook URL", - "placeholder": "https://webhook.example.com/endpoint", - "required": false - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "name": "standards.DeployCheckChromeExtension.webhookEvents", - "label": "Webhook Events", - "placeholder": "e.g. pageBlocked, pageAllowed" - }, - { - "type": "autoComplete", - "multiple": true, - "creatable": true, - "required": false, - "freeSolo": true, - "name": "standards.DeployCheckChromeExtension.urlAllowlist", - "label": "URL Allowlist", - "placeholder": "e.g. https://example.com/*", - "helperText": "Enter URLs to allowlist in the extension. Press enter to add each URL. Wildcards are allowed. This should be used for sites that are being blocked by the extension but are known to be safe." - }, - { - "type": "switch", - "name": "standards.DeployCheckChromeExtension.domainSquattingEnabled", - "label": "Enable domain squatting detection", - "defaultValue": true - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.companyName", - "label": "Company Name", - "placeholder": "YOUR-COMPANY", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.companyURL", - "label": "Company URL", - "placeholder": "https://yourcompany.com", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.productName", - "label": "Product Name", - "placeholder": "YOUR-PRODUCT-NAME", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.supportEmail", - "label": "Support Email", - "placeholder": "support@yourcompany.com", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.supportUrl", - "label": "Support URL", - "placeholder": "https://support.yourcompany.com", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.privacyPolicyUrl", - "label": "Privacy Policy URL", - "placeholder": "https://yourcompany.com/privacy", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.aboutUrl", - "label": "About URL", - "placeholder": "https://yourcompany.com/about", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.primaryColor", - "label": "Primary Color", - "placeholder": "#F77F00", - "required": false - }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.logoUrl", - "label": "Logo URL", - "placeholder": "https://yourcompany.com/logo.png", - "required": false - }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.showNotifications", "label": "Show notifications", "defaultValue": true }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.enableValidPageBadge", "label": "Enable valid page badge", "defaultValue": false }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.enablePageBlocking", "label": "Enable page blocking", "defaultValue": true }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.forceToolbarPin", "label": "Force pin extension to toolbar", "defaultValue": false }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.enableCippReporting", "label": "Enable CIPP reporting", "defaultValue": true }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.customRulesUrl", "label": "Custom Rules URL", "placeholder": "https://YOUR-CIPP-SERVER-URL/rules.json", "helperText": "Enter the URL for custom rules if you have them. This should point to a JSON file with the same structure as the rules.json used for CIPP reporting.", "required": false }, + { "type": "number", "name": "standards.DeployCheckChromeExtension.updateInterval", "label": "Update interval (hours)", "defaultValue": 24 }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.enableDebugLogging", "label": "Enable debug logging", "defaultValue": false }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.enableGenericWebhook", "label": "Enable generic webhook", "defaultValue": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.webhookUrl", "label": "Webhook URL", "placeholder": "https://webhook.example.com/endpoint", "required": false }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "name": "standards.DeployCheckChromeExtension.webhookEvents", "label": "Webhook Events", "placeholder": "e.g. pageBlocked, pageAllowed" }, + { "type": "autoComplete", "multiple": true, "creatable": true, "required": false, "freeSolo": true, "name": "standards.DeployCheckChromeExtension.urlAllowlist", "label": "URL Allowlist", "placeholder": "e.g. https://example.com/*", "helperText": "Enter URLs to allowlist in the extension. Press enter to add each URL. Wildcards are allowed. This should be used for sites that are being blocked by the extension but are known to be safe." }, + { "type": "switch", "name": "standards.DeployCheckChromeExtension.domainSquattingEnabled", "label": "Enable domain squatting detection", "defaultValue": true }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.companyName", "label": "Company Name", "placeholder": "YOUR-COMPANY", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.companyURL", "label": "Company URL", "placeholder": "https://yourcompany.com", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.productName", "label": "Product Name", "placeholder": "YOUR-PRODUCT-NAME", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.supportEmail", "label": "Support Email", "placeholder": "support@yourcompany.com", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.supportUrl", "label": "Support URL", "placeholder": "https://support.yourcompany.com", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.privacyPolicyUrl", "label": "Privacy Policy URL", "placeholder": "https://yourcompany.com/privacy", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.aboutUrl", "label": "About URL", "placeholder": "https://yourcompany.com/about", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.primaryColor", "label": "Primary Color", "placeholder": "#F77F00", "required": false }, + { "type": "textField", "name": "standards.DeployCheckChromeExtension.logoUrl", "label": "Logo URL", "placeholder": "https://yourcompany.com/logo.png", "required": false }, { "name": "AssignTo", "label": "Who should this app be assigned to?", "type": "radio", "options": [ - { - "label": "Do not assign", - "value": "On" - }, - { - "label": "Assign to all users", - "value": "allLicensedUsers" - }, - { - "label": "Assign to all devices", - "value": "AllDevices" - }, - { - "label": "Assign to all users and devices", - "value": "AllDevicesAndUsers" - }, - { - "label": "Assign to Custom Group", - "value": "customGroup" - } + { "label": "Do not assign", "value": "On" }, + { "label": "Assign to all users", "value": "allLicensedUsers" }, + { "label": "Assign to all devices", "value": "AllDevices" }, + { "label": "Assign to all users and devices", "value": "AllDevicesAndUsers" }, + { "label": "Assign to Custom Group", "value": "customGroup" } ] }, - { - "type": "textField", - "required": false, - "name": "customGroup", - "label": "Enter the custom group name if you selected 'Assign to Custom Group'. Wildcards are allowed." - } + { "type": "textField", "required": false, "name": "customGroup", "label": "Enter the custom group name if you selected 'Assign to Custom Group'. Wildcards are allowed." } ], "label": "Deploy Check by CyberDrain Browser Extension", "impact": "Low Impact", "impactColour": "info", "addedDate": "2025-09-18", "powershellEquivalent": "Add-CIPPW32ScriptApplication", - "recommendedBy": ["CIPP"] + "recommendedBy": ["CIPP"], + "requiredCapabilities": ["INTUNE_A", "MDM_Services", "EMS", "SCCM", "MICROSOFTINTUNEPLAN1"] }, { "name": "standards.SecureScoreRemediation", @@ -6121,11 +4163,7 @@ "required": false, "name": "standards.SecureScoreRemediation.Default", "label": "Controls to set to Default", - "api": { - "url": "/secureScore.json", - "labelField": "title", - "valueField": "id" - } + "api": { "url": "/secureScore.json", "labelField": "title", "valueField": "id" } }, { "type": "autoComplete", @@ -6134,11 +4172,7 @@ "required": false, "name": "standards.SecureScoreRemediation.Ignored", "label": "Controls to set to Ignored", - "api": { - "url": "/secureScore.json", - "labelField": "title", - "valueField": "id" - } + "api": { "url": "/secureScore.json", "labelField": "title", "valueField": "id" } }, { "type": "autoComplete", @@ -6147,11 +4181,7 @@ "required": false, "name": "standards.SecureScoreRemediation.ThirdParty", "label": "Controls to set to Third-Party", - "api": { - "url": "/secureScore.json", - "labelField": "title", - "valueField": "id" - } + "api": { "url": "/secureScore.json", "labelField": "title", "valueField": "id" } }, { "type": "autoComplete", @@ -6160,11 +4190,7 @@ "creatable": true, "name": "standards.SecureScoreRemediation.Reviewed", "label": "Controls to set to Reviewed", - "api": { - "url": "/secureScore.json", - "labelField": "title", - "valueField": "id" - } + "api": { "url": "/secureScore.json", "labelField": "title", "valueField": "id" } } ], "label": "Update Secure Score Control Profiles", @@ -6181,50 +4207,20 @@ "docsDescription": "Creates five Exchange Online transport rules grouped by the first letter of user display names (A-E, F-J, K-O, P-T, U-Z). Each rule fires when an external sender's From header matches a display name in that group, prepends a configurable HTML warning banner, and skips emails from accepted organisational domains. Any manually configured sender or domain exemptions on existing rules are preserved when the standard runs. The disclaimer HTML is fully customisable via the standard settings.", "executiveText": "Protects staff from display-name impersonation attacks by injecting a visible warning banner on emails that appear to come from a colleague but originate externally. Rules are maintained automatically across all letter groups and updated whenever the standard runs.", "addedComponent": [ - { - "type": "heading", - "label": "Alert Banner (HTML)", - "required": false - }, - { - "type": "textField", - "name": "standards.ColleagueImpersonationAlert.disclaimerHtml", - "label": "Disclaimer HTML – Paste the full HTML for the warning banner", - "required": true - }, - { - "type": "heading", - "label": "Keyword Exclusions (Exclude certain users by keywords)", - "required": false - }, - { - "type": "autoComplete", - "name": "standards.ColleagueImpersonationAlert.excludedMailboxes", - "label": "Exclude mailboxes by keywords for example any Displayname starting with (Leaver)", - "multiple": true, - "creatable": true, - "required": false - }, - { - "type": "heading", - "label": "Exempt Senders (Email Accounts)", - "required": false - }, - { - "type": "autoComplete", - "name": "standards.ColleagueImpersonationAlert.additionalExemptSenders", - "label": "Additional exempt sender addresses", - "multiple": true, - "creatable": true, - "required": false - } + { "type": "heading", "label": "Alert Banner (HTML)", "required": false }, + { "type": "textField", "name": "standards.ColleagueImpersonationAlert.disclaimerHtml", "label": "Disclaimer HTML – Paste the full HTML for the warning banner", "required": true }, + { "type": "heading", "label": "Keyword Exclusions (Exclude certain users by keywords)", "required": false }, + { "type": "autoComplete", "name": "standards.ColleagueImpersonationAlert.excludedMailboxes", "label": "Exclude mailboxes by keywords for example any Displayname starting with (Leaver)", "multiple": true, "creatable": true, "required": false }, + { "type": "heading", "label": "Exempt Senders (Email Accounts)", "required": false }, + { "type": "autoComplete", "name": "standards.ColleagueImpersonationAlert.additionalExemptSenders", "label": "Additional exempt sender addresses", "multiple": true, "creatable": true, "required": false } ], "label": "Colleague Impersonation Alert Transport Rules", "impact": "Medium Impact", "impactColour": "warning", "addedDate": "2026-03-22", "powershellEquivalent": "New-TransportRule / Set-TransportRule", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] }, { "name": "standards.DefenderCompliancePolicy", @@ -6234,102 +4230,22 @@ "docsDescription": "Configures the Microsoft Defender for Endpoint mobile threat defense connector with Intune. This enables compliance evaluation across platforms (Android, iOS/iPadOS, macOS, Windows) and controls settings like blocking unsupported OS versions, requiring partner data for compliance, and enabling mobile application management. The connector must be enabled before platform-specific compliance policies can evaluate device risk from MDE.", "executiveText": "Establishes the critical link between Microsoft Defender for Endpoint and Intune, enabling security risk data from MDE to be used in device compliance policies. This ensures that only devices meeting your organization's security standards can access corporate resources, providing a foundational layer of Zero Trust security across all platforms.", "addedComponent": [ - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.ConnectAndroid", - "label": "Connect Android devices to MDE", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.ConnectAndroidCompliance", - "label": "Connect Android 6.0.0+ (App-based MAM)", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.androidDeviceBlockedOnMissingPartnerData", - "label": "Block Android if partner data unavailable", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.ConnectIos", - "label": "Connect iOS/iPadOS devices to MDE", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.ConnectIosCompliance", - "label": "Connect iOS 13.0+ (App-based MAM)", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.appSync", - "label": "Enable App Sync for iOS", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.iosDeviceBlockedOnMissingPartnerData", - "label": "Block iOS if partner data unavailable", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosCertificateMetadata", - "label": "Collect certificate metadata from iOS", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosPersonalCertificateMetadata", - "label": "Collect personal certificate metadata from iOS", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.ConnectMac", - "label": "Connect macOS devices to MDE", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.macDeviceBlockedOnMissingPartnerData", - "label": "Block macOS if partner data unavailable", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.ConnectWindows", - "label": "Connect Windows 10.0.15063+ to MDE", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.windowsMobileApplicationManagementEnabled", - "label": "Connect Windows (MAM)", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.windowsDeviceBlockedOnMissingPartnerData", - "label": "Block Windows if partner data unavailable", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.BlockunsupportedOS", - "label": "Block unsupported OS versions", - "defaultValue": false - }, - { - "type": "switch", - "name": "standards.DefenderCompliancePolicy.AllowMEMEnforceCompliance", - "label": "Allow MEM enforcement of compliance", - "defaultValue": false - } + { "type": "switch", "name": "standards.DefenderCompliancePolicy.ConnectAndroid", "label": "Connect Android devices to MDE", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.ConnectAndroidCompliance", "label": "Connect Android 6.0.0+ (App-based MAM)", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.androidDeviceBlockedOnMissingPartnerData", "label": "Block Android if partner data unavailable", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.ConnectIos", "label": "Connect iOS/iPadOS devices to MDE", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.ConnectIosCompliance", "label": "Connect iOS 13.0+ (App-based MAM)", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.appSync", "label": "Enable App Sync for iOS", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.iosDeviceBlockedOnMissingPartnerData", "label": "Block iOS if partner data unavailable", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosCertificateMetadata", "label": "Collect certificate metadata from iOS", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.allowPartnerToCollectIosPersonalCertificateMetadata", "label": "Collect personal certificate metadata from iOS", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.ConnectMac", "label": "Connect macOS devices to MDE", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.macDeviceBlockedOnMissingPartnerData", "label": "Block macOS if partner data unavailable", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.ConnectWindows", "label": "Connect Windows 10.0.15063+ to MDE", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.windowsMobileApplicationManagementEnabled", "label": "Connect Windows (MAM)", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.windowsDeviceBlockedOnMissingPartnerData", "label": "Block Windows if partner data unavailable", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.BlockunsupportedOS", "label": "Block unsupported OS versions", "defaultValue": false }, + { "type": "switch", "name": "standards.DefenderCompliancePolicy.AllowMEMEnforceCompliance", "label": "Allow MEM enforcement of compliance", "defaultValue": false } ], "label": "Defender for Endpoint - Intune Compliance Connector", "impact": "High Impact", @@ -6346,43 +4262,18 @@ "docsDescription": "Configures the Global Quarantine Policy branding and notification settings for the tenant. This includes the quarantine notification sender display name, custom subject line, disclaimer text, the from address used for notifications, and whether to use org branding. Notification frequency is managed separately by the GlobalQuarantineNotifications standard.", "executiveText": "Ensures quarantine notification emails are branded and configured consistently, so end users receive clear, professional alerts about quarantined messages and know how to request release.", "addedComponent": [ - { - "type": "textField", - "name": "standards.GlobalQuarantineSettings.SenderName", - "label": "Sender Display Name (e.g. Contoso-Office365Alerts)", - "helperText": "Will be overridden if an active sender address with an existing display name is used.", - "required": false - }, - { - "type": "textField", - "name": "standards.GlobalQuarantineSettings.CustomSubject", - "label": "Subject", - "required": false - }, - { - "type": "textField", - "name": "standards.GlobalQuarantineSettings.CustomDisclaimer", - "label": "Disclaimer (max 200 characters)", - "required": false - }, - { - "type": "textField", - "name": "standards.GlobalQuarantineSettings.FromAddress", - "label": "Specify Sender Address (must be an internal mailbox)", - "required": false - }, - { - "type": "switch", - "name": "standards.GlobalQuarantineSettings.OrganizationBrandingEnabled", - "label": "Use Organization Branding (logo)", - "helperText": "Requires branding to be configured in the Microsoft 365 admin centre." - } + { "type": "textField", "name": "standards.GlobalQuarantineSettings.SenderName", "label": "Sender Display Name (e.g. Contoso-Office365Alerts)", "helperText": "Will be overridden if an active sender address with an existing display name is used.", "required": false }, + { "type": "textField", "name": "standards.GlobalQuarantineSettings.CustomSubject", "label": "Subject", "required": false }, + { "type": "textField", "name": "standards.GlobalQuarantineSettings.CustomDisclaimer", "label": "Disclaimer (max 200 characters)", "required": false }, + { "type": "textField", "name": "standards.GlobalQuarantineSettings.FromAddress", "label": "Specify Sender Address (must be an internal mailbox)", "required": false }, + { "type": "switch", "name": "standards.GlobalQuarantineSettings.OrganizationBrandingEnabled", "label": "Use Organization Branding (logo)", "helperText": "Requires branding to be configured in the Microsoft 365 admin centre." } ], "label": "Configure Global Quarantine Notification Settings", "impact": "Low Impact", "impactColour": "info", "addedDate": "2026-04-02", "powershellEquivalent": "Set-QuarantinePolicy (GlobalQuarantinePolicy)", - "recommendedBy": [] + "recommendedBy": [], + "requiredCapabilities": ["EXCHANGE_S_STANDARD", "EXCHANGE_S_ENTERPRISE", "EXCHANGE_S_STANDARD_GOV", "EXCHANGE_S_ENTERPRISE_GOV", "EXCHANGE_LITE"] } -] +] \ No newline at end of file From 1e3624cdc1ee722bb4b22bc61167b5a68d2294ac Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:02:49 +0800 Subject: [PATCH 19/30] exchange is silly --- src/pages/email/administration/quarantine/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/email/administration/quarantine/index.js b/src/pages/email/administration/quarantine/index.js index 92aac9f435c0..0aa088af9a58 100644 --- a/src/pages/email/administration/quarantine/index.js +++ b/src/pages/email/administration/quarantine/index.js @@ -126,7 +126,7 @@ const Page = () => { }, confirmText: "Are you sure you want to deny this message?", icon: , - condition: (row) => row.ReleaseStatus !== "DENIED", + condition: (row) => row.ReleaseStatus === "REQUESTED", }, { label: "Release & Allow Sender", @@ -136,6 +136,8 @@ const Page = () => { Identity: "Identity", Type: "!Release", AllowSender: true, + SenderAddress: "SenderAddress", + PolicyName: "PolicyName", }, confirmText: "Are you sure you want to release this email and add the sender to the whitelist?", From c7ad85373ba133afb9aeb35499f5e12d62401ade Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:41:19 +0800 Subject: [PATCH 20/30] Fix: Domain template not getting set if domain already set --- src/components/CippFormPages/CippAddEditUser.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CippFormPages/CippAddEditUser.jsx b/src/components/CippFormPages/CippAddEditUser.jsx index a3dfde804c1c..0706421232a0 100644 --- a/src/components/CippFormPages/CippAddEditUser.jsx +++ b/src/components/CippFormPages/CippAddEditUser.jsx @@ -247,7 +247,7 @@ const CippAddEditUser = (props) => { typeof template.primDomain === 'string' ? { label: template.primDomain, value: template.primDomain } : template.primDomain - setFieldIfEmpty('primDomain', primDomainValue) + formControl.setValue('primDomain', primDomainValue) } if (template.usageLocation) { // Handle both object and string formats From 045e9be901b6739a7686e4b605b86223ec683e23 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 21 Apr 2026 17:19:39 +0800 Subject: [PATCH 21/30] Fix: Render loops --- .../CippComponents/CippAutocomplete.jsx | 85 ++++++++++--------- .../CippFormPages/CippAddEditUser.jsx | 54 ++++++++---- 2 files changed, 85 insertions(+), 54 deletions(-) diff --git a/src/components/CippComponents/CippAutocomplete.jsx b/src/components/CippComponents/CippAutocomplete.jsx index c19376abbebd..8afcb74d7415 100644 --- a/src/components/CippComponents/CippAutocomplete.jsx +++ b/src/components/CippComponents/CippAutocomplete.jsx @@ -124,7 +124,17 @@ export const CippAutoComplete = React.forwardRef((props, ref) => { useEffect(() => { const currentValue = value !== undefined && value !== null ? value : defaultValue if (currentValue !== undefined && currentValue !== null) { - setInternalValue(currentValue) + setInternalValue((prev) => { + // Compare by value to avoid infinite re-render loops when the parent + // passes an object with the same contents but a new reference. + if (prev && typeof prev === 'object' && typeof currentValue === 'object') { + if (prev.value === currentValue.value && prev.label === currentValue.label) { + return prev + } + } + if (prev === currentValue) return prev + return currentValue + }) } }, [value, defaultValue]) @@ -144,23 +154,30 @@ export const CippAutoComplete = React.forwardRef((props, ref) => { } }, [actionGetRequest.data?.pages?.length, actionGetRequest.isFetching, api?.queryKey]) + const apiRef = useRef(api) + apiRef.current = api + + const apiUrl = api?.url + const apiQueryKey = api?.queryKey useEffect(() => { - if (api) { + const currentApi = apiRef.current + if (currentApi) { setGetRequestInfo({ - url: api.url, + url: currentApi.url, data: { - ...(!api.excludeTenantFilter ? { tenantFilter: currentTenant } : null), - ...api.data, + ...(!currentApi.excludeTenantFilter ? { tenantFilter: currentTenant } : null), + ...currentApi.data, }, waiting: true, - queryKey: api.queryKey, + queryKey: currentApi.queryKey, }) } - }, [api, currentTenant]) + }, [apiUrl, apiQueryKey, currentTenant]) // After the data is fetched, combine and map it useEffect(() => { if (actionGetRequest.isSuccess) { + const currentApi = apiRef.current // E.g., allPages is an array of pages returned by the pagination const allPages = actionGetRequest.data?.pages || [] @@ -181,7 +198,7 @@ export const CippAutoComplete = React.forwardRef((props, ref) => { // Flatten the results from all pages const combinedResults = allPages.flatMap((page) => { - const nestedData = getNestedValue(page, api?.dataKey) + const nestedData = getNestedValue(page, currentApi?.dataKey) return nestedData !== undefined ? nestedData : [] }) @@ -196,38 +213,38 @@ export const CippAutoComplete = React.forwardRef((props, ref) => { // Convert each item into your { label, value, addedFields, rawData } shape const convertedOptions = combinedResults.map((option) => { const addedFields = {} - if (api?.addedField) { - Object.keys(api.addedField).forEach((key) => { - addedFields[key] = option[api.addedField[key]] + if (currentApi?.addedField) { + Object.keys(currentApi.addedField).forEach((key) => { + addedFields[key] = option[currentApi.addedField[key]] }) } return { label: - typeof api?.labelField === 'function' - ? api.labelField(option) - : option[api?.labelField] - ? option[api?.labelField] - : option[api?.altLabelField] || - option[api?.valueField] || + typeof currentApi?.labelField === 'function' + ? currentApi.labelField(option) + : option[currentApi?.labelField] + ? option[currentApi?.labelField] + : option[currentApi?.altLabelField] || + option[currentApi?.valueField] || 'No label found - Are you missing a labelField?', value: - typeof api?.valueField === 'function' - ? api.valueField(option) - : option[api?.valueField], + typeof currentApi?.valueField === 'function' + ? currentApi.valueField(option) + : option[currentApi?.valueField], description: - typeof api?.descriptionField === 'function' - ? api.descriptionField(option) - : api?.descriptionField - ? option[api?.descriptionField] + typeof currentApi?.descriptionField === 'function' + ? currentApi.descriptionField(option) + : currentApi?.descriptionField + ? option[currentApi?.descriptionField] : undefined, addedFields, rawData: option, // Store the full original object } }) - if (api?.dataFilter) { - setUsedOptions(api.dataFilter(convertedOptions)) + if (currentApi?.dataFilter) { + setUsedOptions(currentApi.dataFilter(convertedOptions)) } else { setUsedOptions(convertedOptions) } @@ -237,17 +254,7 @@ export const CippAutoComplete = React.forwardRef((props, ref) => { if (actionGetRequest.isError) { setUsedOptions([{ label: getCippError(actionGetRequest.error), value: 'error' }]) } - }, [ - api, - actionGetRequest.data, - actionGetRequest.isSuccess, - actionGetRequest.isError, - preselectedValue, - defaultValue, - value, - multiple, - onChange, - ]) + }, [actionGetRequest.data, actionGetRequest.isSuccess, actionGetRequest.isError, actionGetRequest.error, apiRef]) const memoizedOptions = useMemo(() => { let finalOptions = api ? usedOptions : options @@ -675,10 +682,10 @@ export const CippAutoComplete = React.forwardRef((props, ref) => { }, }, }} - renderOption={(props, option) => { + renderOption={(props, option, { index }) => { const { key, ...optionProps } = props return ( - + {option.label} {option.description && ( diff --git a/src/components/CippFormPages/CippAddEditUser.jsx b/src/components/CippFormPages/CippAddEditUser.jsx index 0706421232a0..1a6569577445 100644 --- a/src/components/CippFormPages/CippAddEditUser.jsx +++ b/src/components/CippFormPages/CippAddEditUser.jsx @@ -10,7 +10,7 @@ import { Grid } from '@mui/system' import { ApiGetCall } from '../../api/ApiCall' import { useSettings } from '../../hooks/use-settings' import { useWatch } from 'react-hook-form' -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { useRouter } from 'next/router' import { Sync } from '@mui/icons-material' @@ -83,7 +83,31 @@ const CippAddEditUser = (props) => { return [] }, [tenantGroups.isSuccess, userGroups.isSuccess, tenantGroups.data, userGroups.data]) - const watcher = useWatch({ control: formControl.control }) + const watcher = useWatch({ + control: formControl.control, + name: ['givenName', 'surname', 'userTemplate', 'AddToGroups'], + }) + + // Debounce givenName/surname so auto-generated displayName/username + // don't trigger setValue on every keystroke + const rawGivenName = watcher[0] + const rawSurname = watcher[1] + const [debouncedName, setDebouncedName] = useState({ givenName: rawGivenName, surname: rawSurname }) + const debounceRef = useRef(null) + useEffect(() => { + debounceRef.current = setTimeout(() => { + setDebouncedName({ givenName: rawGivenName, surname: rawSurname }) + }, 250) + return () => clearTimeout(debounceRef.current) + }, [rawGivenName, rawSurname]) + + // useWatch with a name array returns values in the same order + const watchedFields = { + givenName: debouncedName.givenName, + surname: debouncedName.surname, + userTemplate: watcher[2], + AddToGroups: watcher[3], + } // Helper function to generate username from template format const generateUsername = ( @@ -142,11 +166,11 @@ const CippAddEditUser = (props) => { useEffect(() => { //if watch.firstname changes, and watch.lastname changes, set displayname to firstname + lastname - if (watcher.givenName && watcher.surname && formType === 'add') { + if (watchedFields.givenName && watchedFields.surname && formType === 'add') { // Only auto-set display name if user hasn't manually changed it if (!displayNameManuallySet) { // Build base display name from first and last name - let displayName = `${watcher.givenName} ${watcher.surname}` + let displayName = `${watchedFields.givenName} ${watchedFields.surname}` // Add template displayName as suffix if it exists if (selectedTemplate?.displayName) { @@ -181,8 +205,8 @@ const CippAddEditUser = (props) => { const generatedUsername = generateUsername( formatString, - watcher.givenName, - watcher.surname, + watchedFields.givenName, + watchedFields.surname, spaceHandling, spaceReplacement ) @@ -192,11 +216,11 @@ const CippAddEditUser = (props) => { } } } - }, [watcher.givenName, watcher.surname, selectedTemplate]) + }, [watchedFields.givenName, watchedFields.surname, selectedTemplate]) // Reset manual flags and selected template when form is reset (fields become empty) useEffect(() => { - if (formType === 'add' && !watcher.givenName && !watcher.surname && !watcher.userTemplate) { + if (formType === 'add' && !watchedFields.givenName && !watchedFields.surname && !watchedFields.userTemplate) { setDisplayNameManuallySet(false) setUsernameManuallySet(false) // Only clear selected template if it's not the default template @@ -204,11 +228,11 @@ const CippAddEditUser = (props) => { setSelectedTemplate(null) } } - }, [watcher.givenName, watcher.surname, watcher.userTemplate, formType, selectedTemplate]) + }, [watchedFields.givenName, watchedFields.surname, watchedFields.userTemplate, formType, selectedTemplate]) // Auto-select default template for tenant useEffect(() => { - if (formType === 'add' && userTemplates.isSuccess && !watcher.userTemplate) { + if (formType === 'add' && userTemplates.isSuccess && !watchedFields.userTemplate) { const defaultTemplate = userTemplates.data?.find( (template) => template.defaultForTenant === true ) @@ -225,8 +249,8 @@ const CippAddEditUser = (props) => { // Auto-populate fields when template selected useEffect(() => { - if (formType === 'add' && watcher.userTemplate?.addedFields) { - const template = watcher.userTemplate.addedFields + if (formType === 'add' && watchedFields.userTemplate?.addedFields) { + const template = watchedFields.userTemplate.addedFields setSelectedTemplate(template) // Reset manual edit flags when template changes @@ -235,7 +259,7 @@ const CippAddEditUser = (props) => { // Only set fields if they don't already have values (don't override user input) const setFieldIfEmpty = (fieldName, value) => { - if (!watcher[fieldName] && value) { + if (value) { formControl.setValue(fieldName, value) } } @@ -299,14 +323,14 @@ const CippAddEditUser = (props) => { } }) if (groups.length > 0) { - const currentGroups = watcher.AddToGroups + const currentGroups = watchedFields.AddToGroups if (!currentGroups || (Array.isArray(currentGroups) && currentGroups.length === 0)) { formControl.setValue('AddToGroups', groups, { shouldDirty: true }) } } } } - }, [watcher.userTemplate, formType]) + }, [watchedFields.userTemplate, formType]) return ( From d7a26a62785d573ac9f7d10e48fbde462f5f11d8 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:33:40 +0800 Subject: [PATCH 22/30] Duplicate forwarding entry causing 2 scheduler entries --- .../CippWizardVacationConfirmation.jsx | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/components/CippWizard/CippWizardVacationConfirmation.jsx b/src/components/CippWizard/CippWizardVacationConfirmation.jsx index aa4867bfcb71..4c1e81483a3c 100644 --- a/src/components/CippWizard/CippWizardVacationConfirmation.jsx +++ b/src/components/CippWizard/CippWizardVacationConfirmation.jsx @@ -105,32 +105,6 @@ export const CippWizardVacationConfirmation = (props) => { }) } - if (values.enableForwarding) { - const forwardingData = { - tenantFilter, - Users: values.Users, - forwardOption: values.forwardOption, - KeepCopy: values.forwardKeepCopy || false, - startDate: values.startDate, - endDate: values.endDate, - reference: values.reference || null, - postExecution: values.postExecution || [], - } - - if (values.forwardOption === 'internalAddress') { - forwardingData.ForwardInternal = values.forwardInternal - } - - if (values.forwardOption === 'ExternalAddress') { - forwardingData.ForwardExternal = values.forwardExternal - } - - forwardingVacation.mutate({ - url: '/api/ExecScheduleForwardingVacation', - data: forwardingData, - }) - } - if (values.enableOOO) { const oooData = { tenantFilter, From 442ef3852eb979848c85290d02207c60faddefd9 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 21 Apr 2026 13:51:37 -0400 Subject: [PATCH 23/30] feat: Add per-standard alignment view Introduce a granular (per-standard) view for the Standard & Drift Alignment page with a toggle between Summary and Per Standard modes. Changes include: new imports (MUI, heroicons, standards data), stateful mode toggle, separate filter/action/column sets for summary vs granular, API param/granular queryKey, and an off-canvas details panel that parses/compares expected vs current values and shows formatted diffs. Also replaced Delete icon usage with Edit for template editing. Updated get-cipp-formatting to render complianceStatus as colored Chips and to resolve standardName labels from data/standards.json. fixes #5894 --- src/pages/tenant/standards/alignment/index.js | 436 +++++++++++++++++- src/utils/get-cipp-formatting.js | 24 + 2 files changed, 445 insertions(+), 15 deletions(-) diff --git a/src/pages/tenant/standards/alignment/index.js b/src/pages/tenant/standards/alignment/index.js index a2c67164ed2c..c73e5c1aec05 100644 --- a/src/pages/tenant/standards/alignment/index.js +++ b/src/pages/tenant/standards/alignment/index.js @@ -1,14 +1,18 @@ import { CippTablePage } from '../../../../components/CippComponents/CippTablePage.jsx' import { Layout as DashboardLayout } from '../../../../layouts/index.js' import { TabbedLayout } from '../../../../layouts/TabbedLayout' -import { Delete, Add } from '@mui/icons-material' -import { EyeIcon } from '@heroicons/react/24/outline' +import { Delete, Edit } from '@mui/icons-material' +import { EyeIcon, ListBulletIcon, ChartBarIcon } from '@heroicons/react/24/outline' import tabOptions from '../tabOptions.json' +import { useState } from 'react' +import { Box, Chip, Divider, Stack, Tooltip, Typography } from '@mui/material' +import standardsData from '../../../../data/standards.json' const Page = () => { const pageTitle = 'Standard & Drift Alignment' + const [granular, setGranular] = useState(false) - const filterList = [ + const summaryFilterList = [ { filterName: 'Drift Templates', value: [{ id: 'standardType', value: 'drift' }], @@ -21,7 +25,25 @@ const Page = () => { }, ] - const actions = [ + const granularFilterList = [ + { + filterName: 'Non-Compliant', + value: [{ id: 'complianceStatus', value: 'Non-Compliant' }], + type: 'column', + }, + { + filterName: 'Compliant', + value: [{ id: 'complianceStatus', value: 'Compliant' }], + type: 'column', + }, + { + filterName: 'License Missing', + value: [{ id: 'complianceStatus', value: 'License Missing' }], + type: 'column', + }, + ] + + const summaryActions = [ { label: 'View Tenant Report', link: '/tenant/manage/applied-standards/?tenantFilter=[tenantFilter]&templateId=[standardId]', @@ -29,6 +51,13 @@ const Page = () => { color: 'info', target: '_self', }, + { + label: 'Edit Template', + link: '/tenant/standards/templates/template?id=[standardId]&type=[standardType]', + icon: , + color: 'success', + target: '_self', + }, { label: 'Manage Drift', link: '/tenant/manage/drift?templateId=[standardId]&tenantFilter=[tenantFilter]', @@ -53,22 +82,399 @@ const Page = () => { }, ] + const granularActions = [ + { + label: 'View Tenant Report', + link: '/tenant/manage/applied-standards/?tenantFilter=[tenantFilter]&templateId=[templateId]', + icon: , + color: 'info', + target: '_self', + }, + { + label: 'Edit Template', + link: '/tenant/standards/templates/template?id=[templateId]&type=[standardType]', + icon: , + color: 'success', + target: '_self', + }, + { + label: 'Manage Drift', + link: '/tenant/manage/drift?templateId=[templateId]&tenantFilter=[tenantFilter]', + icon: , + color: 'info', + target: '_self', + condition: (row) => row.standardType === 'drift', + }, + ] + + const parseValue = (value) => { + if (value === null || value === undefined || value === '') return null + if (typeof value === 'string') { + try { + return JSON.parse(value) + } catch { + return value + } + } + return value + } + + const normalizeObj = (val) => { + if (Array.isArray(val)) return val.map(normalizeObj) + if (val !== null && typeof val === 'object') { + return Object.fromEntries( + Object.keys(val) + .sort() + .map((k) => [k, normalizeObj(val[k])]) + ) + } + return val + } + + const compareValues = (expected, current) => { + if (!expected || !current) return null + try { + const expectedObj = normalizeObj( + typeof expected === 'object' ? expected : JSON.parse(expected) + ) + const currentObj = normalizeObj(typeof current === 'object' ? current : JSON.parse(current)) + if (JSON.stringify(expectedObj) === JSON.stringify(currentObj)) return null + const differences = {} + const allKeys = new Set([...Object.keys(expectedObj), ...Object.keys(currentObj)]) + allKeys.forEach((key) => { + const e = normalizeObj(expectedObj[key]) + const c = normalizeObj(currentObj[key]) + if (JSON.stringify(e) !== JSON.stringify(c)) { + differences[key] = { expected: expectedObj[key], current: currentObj[key] } + } + }) + return Object.keys(differences).length > 0 ? differences : null + } catch { + return null + } + } + + const granularOffCanvas = { + size: 'md', + title: 'Standard Details', + contentPadding: 0, + children: (row) => { + const expectedParsed = parseValue(row.expectedValue) + const currentParsed = parseValue(row.currentValue) + const diffs = compareValues(expectedParsed, currentParsed) + const baseName = row.standardId?.split('.').slice(0, -1).join('.') + const prettyName = + standardsData.find((s) => s.name === row.standardId)?.label ?? + standardsData.find((s) => s.name === baseName)?.label ?? + row.standardName + + const complianceColors = { + compliant: 'success', + 'non-compliant': 'error', + 'license missing': 'warning', + 'reporting disabled': 'default', + } + const statusColor = + complianceColors[String(row.complianceStatus ?? '').toLowerCase()] ?? 'default' + + const properties = [ + { label: 'Standard', value: prettyName }, + { label: 'Status', value: row.complianceStatus, color: statusColor }, + { label: 'Template', value: row.templateName }, + { + label: 'Type', + value: row.standardType === 'drift' ? 'Drift Standard' : 'Classic Standard', + }, + { + label: 'Last Applied', + value: row.latestDataCollection + ? new Date(row.latestDataCollection).toLocaleString() + : 'N/A', + }, + ] + + return ( + + {/* Property list */} + } + sx={{ borderBottom: '1px solid', borderColor: 'divider' }} + > + {properties.map(({ label, value, color }) => ( + + + {label} + + {color ? ( + + ) : ( + + {value ?? 'N/A'} + + )} + + ))} + + + {/* Diff / value content */} + {(expectedParsed || currentParsed) && ( + + {diffs ? ( + <> + + Property Differences + + {Object.entries(diffs).map(([key, { expected, current }]) => ( + + + {key} + + + + + Expected + + + + {JSON.stringify(expected, null, 2)} + + + + + + Current + + + + {JSON.stringify(current, null, 2)} + + + + + + ))} + + ) : ( + <> + {expectedParsed !== null && ( + + + Expected + + + + {typeof expectedParsed === 'object' + ? JSON.stringify(expectedParsed, null, 2) + : String(expectedParsed)} + + + + )} + {currentParsed !== null && ( + + + Current + + + + {typeof currentParsed === 'object' + ? JSON.stringify(currentParsed, null, 2) + : String(currentParsed)} + + + + )} + + )} + + )} + + ) + }, + } + + const modeToggle = ( + + + + ) : ( + + ) + } + label={granular ? 'Per Standard' : 'Summary'} + onClick={() => setGranular((v) => !v)} + color="primary" + variant="filled" + size="small" + clickable + /> + + + ) + return ( ) } diff --git a/src/utils/get-cipp-formatting.js b/src/utils/get-cipp-formatting.js index 93f364ca77b2..b900905202ca 100644 --- a/src/utils/get-cipp-formatting.js +++ b/src/utils/get-cipp-formatting.js @@ -33,6 +33,7 @@ import DOMPurify from 'dompurify' import { getSignInErrorCodeTranslation } from './get-cipp-signin-errorcode-translation' import { CollapsibleChipList } from '../components/CippComponents/CollapsibleChipList' import countryList from '../data/countryList.json' +import standardsData from '../data/standards.json' // Helper function to convert country codes to country names const getCountryNameFromCode = (countryCode) => { @@ -436,6 +437,29 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr )) } } + if (cellName === 'complianceStatus') { + if (isText) return data + const complianceColors = { + compliant: 'success', + 'non-compliant': 'error', + 'license missing': 'warning', + 'reporting disabled': 'default', + } + const color = complianceColors[String(data).toLowerCase()] ?? 'default' + return + } + + if (cellName === 'standardName') { + // Already resolved for templates; do a standards.json lookup for classic standards + if (!data?.startsWith('standards.')) return isText ? data : {data} + const baseName = data.split('.').slice(0, -1).join('.') + const label = + standardsData.find((s) => s.name === data)?.label ?? + standardsData.find((s) => s.name === baseName)?.label ?? + data + return label + } + if (cellName === 'standardType') { return isText ? ( data From 691efb87c7e6cbeab6cc4b30281be683ba1c7489 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 21 Apr 2026 13:52:13 -0400 Subject: [PATCH 24/30] fix: Guard API data with Array.isArray checks Use Array.isArray to validate reportListApi.data and reportDataApi.data before mapping or passing to CippDataTable. This replaces the nullish-coalescing fallback so the code won't try to map or render non-array API responses (e.g. objects or other types), avoiding potential runtime errors. --- src/pages/tenant/reports/graph-office-reports/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/tenant/reports/graph-office-reports/index.js b/src/pages/tenant/reports/graph-office-reports/index.js index b97505d12ed4..39bb153c06b6 100644 --- a/src/pages/tenant/reports/graph-office-reports/index.js +++ b/src/pages/tenant/reports/graph-office-reports/index.js @@ -64,7 +64,7 @@ const Page = () => { waiting: !!currentTenant, }) - const reportOptions = (reportListApi.data ?? []).map((r) => ({ + const reportOptions = (Array.isArray(reportListApi.data) ? reportListApi.data : []).map((r) => ({ label: prettifyReportName(r.name), value: r.name, type: r.type ?? null, @@ -156,7 +156,7 @@ const Page = () => { ) : ( Date: Tue, 21 Apr 2026 14:22:30 -0400 Subject: [PATCH 25/30] fix: Update link and condition to use templateType instead of standardType --- src/pages/tenant/standards/alignment/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/tenant/standards/alignment/index.js b/src/pages/tenant/standards/alignment/index.js index c73e5c1aec05..b15a4570991b 100644 --- a/src/pages/tenant/standards/alignment/index.js +++ b/src/pages/tenant/standards/alignment/index.js @@ -92,7 +92,7 @@ const Page = () => { }, { label: 'Edit Template', - link: '/tenant/standards/templates/template?id=[templateId]&type=[standardType]', + link: '/tenant/standards/templates/template?id=[templateId]&type=[templateType]', icon: , color: 'success', target: '_self', @@ -103,7 +103,7 @@ const Page = () => { icon: , color: 'info', target: '_self', - condition: (row) => row.standardType === 'drift', + condition: (row) => row.templateType === 'drift', }, ] From 339efb204b7516458c2e54f2db2702b4bbeb9af7 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:04:34 +0800 Subject: [PATCH 26/30] Cleanup duplicate cache types --- Tests/Shapes/ListTests.json | 4 ---- src/data/CIPPDBCacheTypes.json | 20 -------------------- 2 files changed, 24 deletions(-) diff --git a/Tests/Shapes/ListTests.json b/Tests/Shapes/ListTests.json index 7dd16b29e91d..a4553bd14c70 100644 --- a/Tests/Shapes/ListTests.json +++ b/Tests/Shapes/ListTests.json @@ -103,23 +103,19 @@ "ExoAcceptedDomains": "number", "ExoAdminAuditLogConfig": "number", "ExoAntiPhishPolicies": "number", - "ExoAntiPhishPolicy": "number", "ExoAntiPhishRules": "number", "ExoAtpPolicyForO365": "number", "ExoDkimSigningConfig": "number", "ExoHostedContentFilterPolicy": "number", "ExoHostedOutboundSpamFilterPolicy": "number", "ExoMalwareFilterPolicies": "number", - "ExoMalwareFilterPolicy": "number", "ExoMalwareFilterRules": "number", "ExoOrganizationConfig": "number", "ExoQuarantinePolicy": "number", "ExoRemoteDomain": "number", "ExoSafeAttachmentPolicies": "number", - "ExoSafeAttachmentPolicy": "number", "ExoSafeAttachmentRules": "number", "ExoSafeLinksPolicies": "number", - "ExoSafeLinksPolicy": "number", "ExoSafeLinksRules": "number", "ExoSharingPolicy": "number", "ExoTenantAllowBlockList": "number", diff --git a/src/data/CIPPDBCacheTypes.json b/src/data/CIPPDBCacheTypes.json index 28fac3a6fde8..8ff123ff4aff 100644 --- a/src/data/CIPPDBCacheTypes.json +++ b/src/data/CIPPDBCacheTypes.json @@ -204,26 +204,6 @@ "friendlyName": "Exchange Hosted Outbound Spam Filter Policy", "description": "Exchange Online hosted outbound spam filter policy" }, - { - "type": "ExoAntiPhishPolicy", - "friendlyName": "Exchange Anti-Phish Policy", - "description": "Exchange Online anti-phishing policy" - }, - { - "type": "ExoSafeLinksPolicy", - "friendlyName": "Exchange Safe Links Policy", - "description": "Exchange Online Safe Links policy" - }, - { - "type": "ExoSafeAttachmentPolicy", - "friendlyName": "Exchange Safe Attachment Policy", - "description": "Exchange Online Safe Attachment policy" - }, - { - "type": "ExoMalwareFilterPolicy", - "friendlyName": "Exchange Malware Filter Policy", - "description": "Exchange Online malware filter policy" - }, { "type": "ExoAtpPolicyForO365", "friendlyName": "Exchange ATP Policy for O365", From 9d361bb50c81366558885fa830df5f180be16e33 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:07:48 +0800 Subject: [PATCH 27/30] Remove redundant portal wording --- .../CippComponents/CippTranslations.jsx | 22 ++++++++--------- src/data/portals.json | 24 +++++++++---------- src/pages/cipp/preferences.js | 22 ++++++++--------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/components/CippComponents/CippTranslations.jsx b/src/components/CippComponents/CippTranslations.jsx index af96b19cd027..c4b337ded761 100644 --- a/src/components/CippComponents/CippTranslations.jsx +++ b/src/components/CippComponents/CippTranslations.jsx @@ -27,17 +27,17 @@ export const CippTranslations = { ApplicationID: "Application ID", ApplicationSecret: "Application Secret", GUID: "GUID", - portal_m365: "M365 Portal", - portal_exchange: "Exchange Portal", - portal_entra: "Entra Portal", - portal_teams: "Teams Portal", - portal_azure: "Azure Portal", - portal_intune: "Intune Portal", - portal_security: "Security Portal", - portal_compliance: "Compliance Portal", - portal_sharepoint: "SharePoint Portal", - portal_platform: "Power Platform Portal", - portal_bi: "Power BI Portal", + portal_m365: "M365", + portal_exchange: "Exchange", + portal_entra: "Entra", + portal_teams: "Teams", + portal_azure: "Azure", + portal_intune: "Intune", + portal_security: "Security", + portal_compliance: "Compliance", + portal_sharepoint: "SharePoint", + portal_platform: "Power Platform", + portal_bi: "Power BI", "@odata.type": "Type", roleDefinitionId: "GDAP Role", FromIP: "From IP", diff --git a/src/data/portals.json b/src/data/portals.json index 5c8011ebff77..3d9cdb73b85a 100644 --- a/src/data/portals.json +++ b/src/data/portals.json @@ -1,6 +1,6 @@ [ { - "label": "M365 Portal", + "label": "M365", "name": "M365_Portal", "url": "https://admin.cloud.microsoft/?delegatedOrg=initialDomainName", "variable": "initialDomainName", @@ -9,7 +9,7 @@ "icon": "GlobeAltIcon" }, { - "label": "Exchange Portal", + "label": "Exchange", "name": "Exchange_Portal", "url": "https://admin.cloud.microsoft/exchange?landingpage=homepage&form=mac_sidebar&delegatedOrg=initialDomainName#", "variable": "initialDomainName", @@ -18,7 +18,7 @@ "icon": "Mail" }, { - "label": "Entra Portal", + "label": "Entra", "name": "Entra_Portal", "url": "https://entra.microsoft.com/defaultDomainName", "variable": "defaultDomainName", @@ -27,7 +27,7 @@ "icon": "UsersIcon" }, { - "label": "Teams Portal", + "label": "Teams", "name": "Teams_Portal", "url": "https://admin.teams.microsoft.com/?delegatedOrg=initialDomainName", "variable": "initialDomainName", @@ -36,7 +36,7 @@ "icon": "FilePresent" }, { - "label": "Azure Portal", + "label": "Azure", "name": "Azure_Portal", "url": "https://portal.azure.com/defaultDomainName", "variable": "defaultDomainName", @@ -45,7 +45,7 @@ "icon": "ServerIcon" }, { - "label": "Intune Portal", + "label": "Intune", "name": "Intune_Portal", "url": "https://intune.microsoft.com/defaultDomainName", "variable": "defaultDomainName", @@ -54,7 +54,7 @@ "icon": "Laptop" }, { - "label": "SharePoint Admin", + "label": "SharePoint", "name": "SharePoint_Admin", "url": "/api/ListSharePointAdminUrl?tenantFilter=defaultDomainName", "variable": "defaultDomainName", @@ -63,7 +63,7 @@ "icon": "Share" }, { - "label": "Security Portal", + "label": "Security", "name": "Security_Portal", "url": "https://security.microsoft.com/?tid=customerId", "variable": "customerId", @@ -72,7 +72,7 @@ "icon": "Shield" }, { - "label": "Compliance Portal", + "label": "Compliance", "name": "Compliance_Portal", "url": "https://purview.microsoft.com/?tid=customerId", "variable": "customerId", @@ -81,7 +81,7 @@ "icon": "ShieldMoon" }, { - "label": "Power Platform Portal", + "label": "Power Platform", "name": "Power_Platform_Portal", "url": "https://admin.powerplatform.microsoft.com/account/login/customerId", "variable": "customerId", @@ -90,7 +90,7 @@ "icon": "PrecisionManufacturing" }, { - "label": "Power BI Portal", + "label": "Power BI", "name": "Power_BI_Portal", "url": "https://app.powerbi.com/admin-portal?ctid=customerId", "variable": "customerId", @@ -98,4 +98,4 @@ "external": true, "icon": "BarChart" } -] \ No newline at end of file +] diff --git a/src/pages/cipp/preferences.js b/src/pages/cipp/preferences.js index f6bbde2e7704..2b5303fa8225 100644 --- a/src/pages/cipp/preferences.js +++ b/src/pages/cipp/preferences.js @@ -183,47 +183,47 @@ const Page = () => { const portalLinksConfig = [ { name: "portalLinks.M365_Portal", - label: "M365 Portal", + label: "M365", }, { name: "portalLinks.Exchange_Portal", - label: "Exchange Portal", + label: "Exchange", }, { name: "portalLinks.Entra_Portal", - label: "Entra Portal", + label: "Entra", }, { name: "portalLinks.Teams_Portal", - label: "Teams Portal", + label: "Teams", }, { name: "portalLinks.Azure_Portal", - label: "Azure Portal", + label: "Azure", }, { name: "portalLinks.Intune_Portal", - label: "Intune Portal", + label: "Intune", }, { name: "portalLinks.SharePoint_Admin", - label: "SharePoint Admin", + label: "SharePoint", }, { name: "portalLinks.Security_Portal", - label: "Security Portal", + label: "Security", }, { name: "portalLinks.Compliance_Portal", - label: "Compliance Portal", + label: "Compliance", }, { name: "portalLinks.Power_Platform_Portal", - label: "Power Platform Portal", + label: "Power Platform", }, { name: "portalLinks.Power_BI_Portal", - label: "Power BI Portal", + label: "Power BI", }, ]; From 971eab240876f3d9f11196dfb66b32117a0da7a4 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:29:16 +0800 Subject: [PATCH 28/30] Feat: resizable columns --- src/components/CippTable/CippDataTable.js | 2 + .../CippTable/util-columnsFromAPI.js | 84 ++++++++++--------- src/utils/get-cipp-column-size.js | 10 ++- 3 files changed, 55 insertions(+), 41 deletions(-) diff --git a/src/components/CippTable/CippDataTable.js b/src/components/CippTable/CippDataTable.js index 100837ab17d3..0fc4f87432b7 100644 --- a/src/components/CippTable/CippDataTable.js +++ b/src/components/CippTable/CippDataTable.js @@ -856,6 +856,8 @@ export const CippDataTable = (props) => { layoutMode: 'grid-no-grow', enableRowVirtualization: true, enableColumnVirtualization: true, + enableColumnResizing: true, + columnResizeMode: 'onChange', rowVirtualizerOptions: { overscan: 5, }, diff --git a/src/components/CippTable/util-columnsFromAPI.js b/src/components/CippTable/util-columnsFromAPI.js index b3d3c0e61be5..fa6259e7982b 100644 --- a/src/components/CippTable/util-columnsFromAPI.js +++ b/src/components/CippTable/util-columnsFromAPI.js @@ -9,58 +9,56 @@ const skipRecursion = ['location', 'ScheduledBackupValues', 'Tenant'] const MAX_SIZE_SAMPLE = 30 // Average character width in pixels at compact density (roughly 7–8px per char). const CHAR_WIDTH = 8 -// Extra padding per cell (sort icon, filter icon, cell padding). -const CELL_PADDING = 36 +// Extra padding per cell (sort icon, filter icon, cell padding, resize handle). +const CELL_PADDING = 5 const MIN_COL_SIZE = 80 const MAX_COL_SIZE = 500 +// Extra pixels reserved in the header for MRT chrome (sort icon, column actions menu, +// resize handle, filter icon). These sit alongside the header text and consume space. +const HEADER_CHROME_PX = 75 + // Measure the pixel width a column needs based on its header and sampled cell values. // rawValues are the original data values (before formatting) — if they contain arrays or // complex objects the column renders as a button/chip list, so we cap to header width. -// Returns { size, minSize } where minSize is always header-width + 30px safe space. +// Returns { size, minSize } where minSize is always header-width + chrome safe space. const measureColumnSize = (header, valuesForColumn, rawValues) => { const headerLen = header ? header.length : 6 - const headerPx = Math.round(headerLen * CHAR_WIDTH + CELL_PADDING + 30) + const headerPx = Math.round(headerLen * CHAR_WIDTH + CELL_PADDING + HEADER_CHROME_PX) const minSize = Math.max(MIN_COL_SIZE, headerPx) - // If any raw value is an array or complex object, the cell renders as a compact - // button or chip list. We measure the longest individual chip/item rather than the - // full joined text. For object arrays that format into comma-separated chip labels - // (e.g. assignedLicenses), we split the formatted text on commas to measure each chip. + // If any raw value is an array or complex object, the cell renders as either: + // - A CippDataTableButton ("X items" button) for object arrays and plain objects + // - A CollapsibleChipList for string/primitive arrays + // Size accordingly: buttons are compact, chips need per-item measurement. if (rawValues && rawValues.length > 0) { const hasComplex = rawValues.some( (v) => Array.isArray(v) || (typeof v === 'object' && v !== null) ) if (hasComplex) { + // Check if these are object arrays or plain objects — they render as a small + // "X items" button (CippDataTableButton), so size to the button width. + const allObjectLike = rawValues.every((v) => { + if (v === null || v === undefined) return true // nulls are fine, they show "No items" + if (Array.isArray(v)) return v.some((el) => typeof el === 'object' && el !== null) + return typeof v === 'object' + }) + if (allObjectLike) { + // "X items" button is roughly 80-100px wide — just use header width + return { size: minSize, minSize } + } + + // String/primitive arrays → rendered as chip list. Measure longest chip, + // but cap per-chip text since chips truncate long values (e.g. email addresses). + const MAX_CHIP_TEXT = 15 let longestItem = headerLen for (let i = 0; i < rawValues.length; i++) { const v = rawValues[i] if (Array.isArray(v)) { - const isObjectArray = v.some((el) => typeof el === 'object' && el !== null) - if (isObjectArray) { - // Object arrays get translated by getCippFormatting into comma-joined text. - // Split on ", " and measure each segment (each becomes a chip). - const formatted = valuesForColumn[i] - if (typeof formatted === 'string') { - for (const seg of formatted.split(', ')) { - if (seg.length > longestItem) longestItem = seg.length - } - } - continue - } - // Arrays of strings/numbers → chips — measure each item for (const el of v) { const s = typeof el === 'string' ? el : el != null ? String(el) : '' - if (s.length > longestItem) longestItem = s.length - } - } - // Plain objects → may also format into chip text - if (typeof v === 'object' && v !== null && !Array.isArray(v)) { - const formatted = valuesForColumn[i] - if (typeof formatted === 'string') { - for (const seg of formatted.split(', ')) { - if (seg.length > longestItem) longestItem = seg.length - } + const len = Math.min(s.length, MAX_CHIP_TEXT) + if (len > longestItem) longestItem = len } } } @@ -70,17 +68,27 @@ const measureColumnSize = (header, valuesForColumn, rawValues) => { } } - let maxLen = headerLen const sample = valuesForColumn.length > MAX_SIZE_SAMPLE ? valuesForColumn.slice(0, MAX_SIZE_SAMPLE) : valuesForColumn - for (const v of sample) { - const str = typeof v === 'string' ? v : v != null ? String(v) : '' - // URLs render as icons/links in the cell — don't measure the full URL text. - if (str.match(/^https?:\/\//i) || str.match(/^\/api\//i)) continue - if (str.length > maxLen) maxLen = str.length + const lengths = sample + .map((v) => { + const str = typeof v === 'string' ? v : v != null ? String(v) : '' + // URLs render as icons/links in the cell — don't measure the full URL text. + if (str.match(/^https?:\/\//i) || str.match(/^\/api\//i)) return 0 + return str.length + }) + .sort((a, b) => a - b) + + // Trim the top and bottom 10% to remove outliers, then use the longest remaining value. + let trimmedLengths = lengths + if (lengths.length >= 5) { + const trimCount = Math.max(1, Math.floor(lengths.length * 0.1)) + trimmedLengths = lengths.slice(trimCount, lengths.length - trimCount) } + const maxLen = Math.max(headerLen, ...trimmedLengths) + const px = Math.round(maxLen * CHAR_WIDTH + CELL_PADDING) const size = Math.max(minSize, Math.min(MAX_COL_SIZE, px)) return { size, minSize } @@ -210,7 +218,7 @@ export const utilColumnsFromAPI = (dataArray) => { // Allow per-column size overrides for columns whose rendered output // doesn't match text width (icons, progress bars, etc.). - const sizeOverride = getCippColumnSize(accessorKey) + const sizeOverride = getCippColumnSize(accessorKey, header) let finalSize = { ...measuredSize } if (sizeOverride) { const resolve = (v) => (v === 'header' ? measuredSize.minSize : v) diff --git a/src/utils/get-cipp-column-size.js b/src/utils/get-cipp-column-size.js index 6719d251151a..ecfbd1e1367d 100644 --- a/src/utils/get-cipp-column-size.js +++ b/src/utils/get-cipp-column-size.js @@ -1,10 +1,14 @@ // Returns { size, minSize } overrides for columns where the rendered cell // doesn't match the text-measured width (icons, progress bars, etc.). // Returns null when measurement should be used as-is. -export const getCippColumnSize = (accessorKey) => { - // Portal columns render as a small icon — header width is enough. +// header is the translated column title text. +export const getCippColumnSize = (accessorKey, header) => { + // Portal columns render as a small icon — size based on title length + // plus room for sort icon, column actions, and resize handle. if (accessorKey && accessorKey.startsWith('portal_')) { - return { size: 'header', minSize: 'header' } + const titleLen = header ? header.length : 6 + const px = Math.round(titleLen * 8 + 85) + return { size: px, minSize: px } } // Progress bar / percentage columns need room for the bar component. From 932d591d9f999dd59ec0015d60680f2e9503eb61 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:08:50 +0800 Subject: [PATCH 29/30] Update add.jsx --- src/pages/tools/custom-tests/add.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/tools/custom-tests/add.jsx b/src/pages/tools/custom-tests/add.jsx index fb4387bbbdb8..6951fe57c507 100644 --- a/src/pages/tools/custom-tests/add.jsx +++ b/src/pages/tools/custom-tests/add.jsx @@ -406,7 +406,7 @@ All UPNs: {{join(Result[*].UserPrincipalName, ", ")}}`, placeholder: `# Example: Find disabled users with licenses param($TenantFilter, $DaysThreshold = 30) -$users = New-CIPPDbRequest -TenantFilter $TenantFilter -Type 'Users' +$users = Get-CIPPTestData -TenantFilter $TenantFilter -Type 'Users' $results = $users | Where-Object { $_.assignedLicenses.Count -gt 0 -and $_.accountEnabled -eq $false @@ -655,7 +655,7 @@ return $results`, Data Access - Read-only via New-CIPPDbRequest and Get-CIPPDbItem{' '} + Read-only via Get-CIPPTestData and Get-CIPPDbItem{' '} with a -Type parameter.