From 733ac2441fb45db91ddaf90526e289b913774828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sun, 9 Nov 2025 02:21:04 +0100 Subject: [PATCH 1/3] fix: Exclude bulk registration accounts from MFA alerts and return structured data Fixes #4907 - Exclude package_{GUID}@* accounts from MFA alerts to reduce false positives - Changed alert output from concatenated string to PSCustomObject array - Each alert now includes UserPrincipalName, DisplayName, and LastUpdated --- .../Public/Alerts/Get-CIPPAlertMFAAlertUsers.ps1 | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMFAAlertUsers.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMFAAlertUsers.ps1 index e56ce8fe2b04..32f14ddc1947 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMFAAlertUsers.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertMFAAlertUsers.ps1 @@ -4,7 +4,7 @@ function Get-CIPPAlertMFAAlertUsers { Entrypoint #> [CmdletBinding()] - Param ( + param ( [Parameter(Mandatory = $false)] [Alias('input')] $InputValue, @@ -12,9 +12,16 @@ function Get-CIPPAlertMFAAlertUsers { ) try { - $users = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?`$top=999&filter=IsAdmin eq false and isMfaRegistered eq false and userType eq 'member'&`$select=userDisplayName,userPrincipalName,lastUpdatedDateTime,isMfaRegistered,IsAdmin" -tenantid $($TenantFilter) -AsApp $true | Where-Object { $_.userDisplayName -ne 'On-Premises Directory Synchronization Service Account' } - if ($users.UserPrincipalName) { - $AlertData = "The following $($users.Count) users do not have MFA registered: $($users.UserPrincipalName -join ', ')" + $Users = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?`$top=999&filter=IsAdmin eq false and isMfaRegistered eq false and userType eq 'member'&`$select=userDisplayName,userPrincipalName,lastUpdatedDateTime,isMfaRegistered,IsAdmin" -tenantid $($TenantFilter) -AsApp $true | + Where-Object { $_.userDisplayName -ne 'On-Premises Directory Synchronization Service Account' -and $_.userPrincipalName -notmatch '^package_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}@' } + if ($Users) { + $AlertData = foreach ($user in $Users) { + [PSCustomObject]@{ + UserPrincipalName = $user.userPrincipalName + DisplayName = $user.userDisplayName + LastUpdated = $user.lastUpdatedDateTime + } + } Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData } From 4412abe77d8f1da0344b2d77da7fe17e6585e798 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:34:25 +0000 Subject: [PATCH 2/3] Initial plan From 1e86b4ff6c4928a794476bd0faba35b0353a2c8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:40:36 +0000 Subject: [PATCH 3/3] Add Get-CIPPAlertReportOnlyCA alert function Co-authored-by: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> --- .../Alerts/Get-CIPPAlertReportOnlyCA.ps1 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Modules/CIPPCore/Public/Alerts/Get-CIPPAlertReportOnlyCA.ps1 diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertReportOnlyCA.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertReportOnlyCA.ps1 new file mode 100644 index 000000000000..59de179c2945 --- /dev/null +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertReportOnlyCA.ps1 @@ -0,0 +1,37 @@ +function Get-CIPPAlertReportOnlyCA { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $false)] + [Alias('input')] + $InputValue, + $TenantFilter + ) + + try { + # Only consider CA available when a SKU that grants it has enabled seats (> 0) + $SubscribedSkus = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/subscribedSkus?`$select=prepaidUnits,servicePlans" -tenantid $TenantFilter -ErrorAction Stop + $CAAvailable = foreach ($sku in $SubscribedSkus) { + if ([int]$sku.prepaidUnits.enabled -gt 0) { $sku.servicePlans } + } + + if (('AAD_PREMIUM' -in $CAAvailable.servicePlanName) -or ('AAD_PREMIUM_P2' -in $CAAvailable.servicePlanName)) { + $CAPolicies = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$top=999' -tenantid $TenantFilter -ErrorAction Stop) + + # Filter for policies in report-only mode + $ReportOnlyPolicies = $CAPolicies | Where-Object { $_.state -eq 'enabledForReportingButNotEnforced' } + + if ($ReportOnlyPolicies) { + $PolicyNames = $ReportOnlyPolicies.displayName -join ', ' + $AlertData = "The following Conditional Access policies are in report-only mode: $PolicyNames" + Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData + } + } + } catch { + Write-AlertMessage -tenant $($TenantFilter) -message "Report-Only CA Alert: Error occurred: $(Get-NormalizedError -message $_.Exception.message)" + } + +}