Checks/Azure/Test-EntraPrivilegedUserHasMfa.ps1
|
function Test-EntraPrivilegedUserHasMfa { <# .SYNOPSIS Tests if all privileged users have MFA enabled. .DESCRIPTION This check verifies that users who are assigned to directory roles (privileged users) have MFA registered. This includes users in roles such as Global Administrator, User Access Administrator, Subscription Owner, etc. MFA provides additional security by requiring a second form of authentication, which is especially critical for users with elevated privileges. .PARAMETER CheckMetadata Hashtable containing check metadata including id and severity. .EXAMPLE Test-EntraPrivilegedUserHasMfa -CheckMetadata $metadata #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param( [Parameter(Mandatory)] [hashtable]$CheckMetadata ) $ErrorActionPreference = 'Stop' # Check if required data is available if (-not $script:EntraService.DirectoryRoles) { $findingParams = @{ CheckMetadata = $CheckMetadata Status = 'SKIPPED' StatusExtended = 'Unable to retrieve directory roles - missing permissions' ResourceId = 'N/A' ResourceName = 'Directory Roles' } New-CIEMFinding @findingParams } elseif (-not $script:EntraService.UserMFAStatus) { $findingParams = @{ CheckMetadata = $CheckMetadata Status = 'SKIPPED' StatusExtended = 'Unable to retrieve user MFA registration details - missing permissions' ResourceId = 'N/A' ResourceName = 'User MFA Status' } New-CIEMFinding @findingParams } else { # Build a set of privileged users (users in any directory role) $privilegedUsers = @{} $userRoles = @{} if ($script:EntraService.DirectoryRoleMembers) { foreach ($role in $script:EntraService.DirectoryRoles) { $roleId = $role.id $roleName = $role.displayName $members = $script:EntraService.DirectoryRoleMembers[$roleId] if ($members) { foreach ($member in $members) { if ($member.id -and -not $privilegedUsers.ContainsKey($member.id)) { $privilegedUsers[$member.id] = $member $userRoles[$member.id] = @() } if ($member.id) { $userRoles[$member.id] += $roleName } } } } } if ($privilegedUsers.Count -eq 0) { $findingParams = @{ CheckMetadata = $CheckMetadata Status = 'PASS' StatusExtended = 'No privileged users found in directory roles' ResourceId = 'privileged-users' ResourceName = 'Privileged Users' } New-CIEMFinding @findingParams } else { # Build MFA status lookup $mfaStatusLookup = @{} foreach ($mfaStatus in $script:EntraService.UserMFAStatus) { $mfaStatusLookup[$mfaStatus.id] = $mfaStatus } # Check each privileged user for MFA $usersWithoutMfa = @() $usersWithMfa = @() foreach ($userId in $privilegedUsers.Keys) { $user = $privilegedUsers[$userId] $mfaStatus = $mfaStatusLookup[$userId] # Check if user has MFA methods registered $hasMfa = $false if ($mfaStatus) { # Check isMfaRegistered property or methodsRegistered array if ($mfaStatus.isMfaRegistered -eq $true) { $hasMfa = $true } elseif ($mfaStatus.methodsRegistered -and $mfaStatus.methodsRegistered.Count -gt 0) { # Check for actual MFA methods (not just password) $mfaMethods = $mfaStatus.methodsRegistered | Where-Object { $_ -ne 'password' } if ($mfaMethods.Count -gt 0) { $hasMfa = $true } } } if ($hasMfa) { $usersWithMfa += @{ User = $user Roles = $userRoles[$userId] } } else { $usersWithoutMfa += @{ User = $user Roles = $userRoles[$userId] } } } $totalPrivileged = $privilegedUsers.Count $withoutMfaCount = $usersWithoutMfa.Count if ($withoutMfaCount -eq 0) { $findingParams = @{ CheckMetadata = $CheckMetadata Status = 'PASS' StatusExtended = "All $totalPrivileged privileged users have MFA enabled" ResourceId = 'privileged-users' ResourceName = 'Privileged Users' } New-CIEMFinding @findingParams } else { # Report individual users without MFA foreach ($item in $usersWithoutMfa) { $user = $item.User $roles = $item.Roles -join ', ' $findingParams = @{ CheckMetadata = $CheckMetadata Status = 'FAIL' StatusExtended = "Privileged user '$($user.displayName)' ($($user.userPrincipalName)) does not have MFA enabled. Assigned roles: $roles" ResourceId = $user.id ResourceName = $user.displayName } New-CIEMFinding @findingParams } } } } } |