From 1f86500619071c7eb08b9fbbf9e84a0c32b742f7 Mon Sep 17 00:00:00 2001 From: fit-jv Date: Wed, 15 Apr 2026 15:35:09 +0100 Subject: [PATCH 1/4] "syncInfoNote": "Outlook for iOS and Android uses modern authentication and does not report ActiveSync sync times." Added syncInfoNote Also updated the related function - Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Reports/Invoke-ListActiveSyncDevices.ps1 Signed-off-by: fit-jv --- src/pages/email/reports/activesync-devices/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/email/reports/activesync-devices/index.js b/src/pages/email/reports/activesync-devices/index.js index 38b7ea65f87d..4ddd6306cd99 100644 --- a/src/pages/email/reports/activesync-devices/index.js +++ b/src/pages/email/reports/activesync-devices/index.js @@ -76,6 +76,7 @@ const Page = () => { 'firstSyncTime', 'lastSyncAttemptTime', 'lastSuccessSync', + 'syncInfoNote', 'deviceID', ], actions: actions, From 86190ce230c530aa99e6290ed7d9b2f945c20504 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:09:57 +0200 Subject: [PATCH 2/4] drift deny for compliant and other policies --- src/pages/tenant/manage/drift.js | 78 ++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/src/pages/tenant/manage/drift.js b/src/pages/tenant/manage/drift.js index 015ad73c6000..fdf55e0d2771 100644 --- a/src/pages/tenant/manage/drift.js +++ b/src/pages/tenant/manage/drift.js @@ -1134,6 +1134,17 @@ const ManageDriftPage = () => { const handleDeviationAction = (action, deviation) => { if (!deviation) return + const resolvedReceivedValue = + deviation.receivedValue ?? + deviation.CurrentValue ?? + deviation.currentValue ?? + deviation.originalDeviation?.receivedValue ?? + deviation.originalDeviation?.CurrentValue ?? + deviation.originalDeviation?.currentValue ?? + deviation.expectedValue ?? + deviation.ExpectedValue ?? + null + let status let actionText switch (action) { @@ -1168,7 +1179,7 @@ const ManageDriftPage = () => { { standardName: deviation.standardName, // Use the standardName from the original deviation data status: status, - receivedValue: deviation.receivedValue, + receivedValue: resolvedReceivedValue, }, ], tenantFilter: tenantFilter, @@ -1407,6 +1418,25 @@ const ManageDriftPage = () => { ), })) + // Add action buttons to compliant/aligned items so previously denied and now compliant entries + // can be denied again or denied with remediation persistence. + const alignedItemsWithActions = allAlignedItems.map((item) => ({ + ...item, + cardLabelBoxActions: ( + + ), + })) + // Calculate compliance metrics for badges // Accepted and Customer Specific deviations count as compliant since they are user-approved // Denied deviations are included in total but not in compliant count (they haven't been fixed yet) @@ -1485,7 +1515,7 @@ const ManageDriftPage = () => { const filteredAcceptedItems = applyFilters(acceptedDeviationItemsWithActions) const filteredCustomerSpecificItems = applyFilters(customerSpecificDeviationItemsWithActions) const filteredDeniedItems = applyFilters(deniedDeviationItemsWithActions) - const filteredAlignedItems = applyFilters(allAlignedItems) + const filteredAlignedItems = applyFilters(alignedItemsWithActions) const filteredLicenseSkippedItems = applyFilters(licenseSkippedItems) // Helper function to render items grouped by category when category sort is active @@ -1918,12 +1948,7 @@ const ManageDriftPage = () => { Compliant Standards - + {renderItemsByCategory(filteredAlignedItems)} )} @@ -2148,6 +2173,15 @@ const ManageDriftPage = () => { open={Boolean(anchorEl[`denied-${item.id}`])} onClose={() => handleMenuClose(`denied-${item.id}`)} > + { + handleDeviationAction('deny', item) + handleMenuClose(`denied-${item.id}`) + }} + > + + Rerun standard to align with template + { handleDeviationAction('deny-remediate', item) @@ -2178,6 +2212,34 @@ const ManageDriftPage = () => { ))} + {alignedItemsWithActions.map((item) => ( + handleMenuClose(`aligned-${item.id}`)} + > + { + handleDeviationAction('deny', item) + handleMenuClose(`aligned-${item.id}`) + }} + > + + Rerun standard to align with template + + { + handleDeviationAction('deny-remediate', item) + handleMenuClose(`aligned-${item.id}`) + }} + > + + Deny - Remediate to align with template + + + ))} + {/* Hidden ExecutiveReportButton that gets triggered programmatically */} Date: Thu, 23 Apr 2026 22:25:15 +0800 Subject: [PATCH 3/4] Update add.jsx --- src/pages/identity/administration/jit-admin-templates/add.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/identity/administration/jit-admin-templates/add.jsx b/src/pages/identity/administration/jit-admin-templates/add.jsx index f2549b66ec4d..986e7ea378d1 100644 --- a/src/pages/identity/administration/jit-admin-templates/add.jsx +++ b/src/pages/identity/administration/jit-admin-templates/add.jsx @@ -236,6 +236,10 @@ const Page = () => { validators={{ required: "Expiration action is required" }} /> + + + Date: Thu, 23 Apr 2026 17:53:29 +0200 Subject: [PATCH 4/4] added marker for permanent deny --- src/pages/tenant/manage/drift.js | 69 ++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/pages/tenant/manage/drift.js b/src/pages/tenant/manage/drift.js index fdf55e0d2771..8c383590ed04 100644 --- a/src/pages/tenant/manage/drift.js +++ b/src/pages/tenant/manage/drift.js @@ -107,6 +107,49 @@ const ManageDriftPage = () => { enabled: !!templateId && !!tenantFilter, }) + // API call for persistent drift remediation tasks + const persistentDriftTasksApi = ApiGetCall({ + url: '/api/ListScheduledItems', + data: { + tenantFilter: tenantFilter, + SearchTitle: 'Persistent Drift Remediation:*', + }, + queryKey: `PersistentDriftTasks-${tenantFilter}`, + waiting: !!tenantFilter, + }) + + const persistentTaskNameSet = new Set( + (persistentDriftTasksApi.data || []) + .map((task) => (task?.Name ? String(task.Name).toLowerCase() : null)) + .filter(Boolean) + ) + + const getDriftTaskSettingName = (standardName) => { + if (!standardName) return '' + + const normalizedName = String(standardName) + const withoutPrefix = normalizedName.replace(/^standards\./, '') + + if (withoutPrefix.startsWith('IntuneTemplate.')) { + return 'IntuneTemplate' + } + + if (withoutPrefix.startsWith('ConditionalAccessTemplate.')) { + return 'ConditionalAccessTemplate' + } + + return withoutPrefix + } + + const hasPersistentDenyTask = (standardName) => { + const settingName = getDriftTaskSettingName(standardName) + if (!settingName || !tenantFilter) return false + + const expectedTaskName = + `Persistent Drift Remediation: ${settingName} - ${tenantFilter}`.toLowerCase() + return persistentTaskNameSet.has(expectedTaskName) + } + // Process drift data for chart - filter by current tenant and aggregate const rawDriftData = driftApi.data || [] const tenantDriftData = Array.isArray(rawDriftData) @@ -512,6 +555,7 @@ const ManageDriftPage = () => { : isLicenseSkipped ? 'Skipped - No License Available' : getDeviationStatusText(actualStatus) + const isPersistentDenyEnabled = hasPersistentDenyTask(deviation.standardName) // For skipped items, show different expected/received values let displayExpectedValue = deviation.ExpectedValue || deviation.expectedValue @@ -536,15 +580,29 @@ const ManageDriftPage = () => { text: prettyName, subtext: description, statusColor: isLicenseSkipped ? 'text.secondary' : getDeviationColor(actualStatus), - statusText: actualStatusText, + statusText: isPersistentDenyEnabled + ? `${actualStatusText} | Persistent deny (12h)` + : actualStatusText, standardName: deviation.standardName, // Store the original standardName for action handlers receivedValue: deviation.receivedValue, // Store the original receivedValue for action handlers expectedValue: deviation.expectedValue, // Store the original expectedValue for action handlers originalDeviation: deviation, // Store the complete original deviation object for reference isLicenseSkipped: isLicenseSkipped, // Flag for filtering and disabling actions isActuallyCompliant: isActuallyCompliant, // Flag to move to compliant section + isPersistentDenyEnabled: isPersistentDenyEnabled, children: ( + {isPersistentDenyEnabled && ( + + + + )} + {description && description !== 'No description available' && ( {description} @@ -1599,7 +1657,12 @@ const ManageDriftPage = () => { subtitle={subtitle} actions={actions} actionsData={{}} - isFetching={driftApi.isFetching || standardsApi.isFetching || comparisonApi.isFetching} + isFetching={ + driftApi.isFetching || + standardsApi.isFetching || + comparisonApi.isFetching || + persistentDriftTasksApi.isFetching + } > @@ -2014,7 +2077,7 @@ const ManageDriftPage = () => { }, }} row={actionData.data} - relatedQueryKeys={[`TenantDrift-${tenantFilter}`]} + relatedQueryKeys={[`TenantDrift-${tenantFilter}`, `PersistentDriftTasks-${tenantFilter}`]} /> )}