|
| 1 | +function Invoke-CippTestZTNA21801 { |
| 2 | + <# |
| 3 | + .SYNOPSIS |
| 4 | + Users have strong authentication methods configured |
| 5 | + #> |
| 6 | + param($Tenant) |
| 7 | + |
| 8 | + try { |
| 9 | + $UserRegistrationDetails = New-CIPPDbRequest -TenantFilter $Tenant -Type 'UserRegistrationDetails' |
| 10 | + $Users = New-CIPPDbRequest -TenantFilter $Tenant -Type 'Users' |
| 11 | + |
| 12 | + if (-not $UserRegistrationDetails -or -not $Users) { |
| 13 | + Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21801' -TestType 'Identity' -Status 'Skipped' -ResultMarkdown 'No data found in database. This may be due to missing required licenses or data collection not yet completed.' -Risk 'Medium' -Name 'Users have strong authentication methods configured' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Credential Management' |
| 14 | + return |
| 15 | + } |
| 16 | + |
| 17 | + $PhishResistantMethods = @('passKeyDeviceBound', 'passKeyDeviceBoundAuthenticator', 'windowsHelloForBusiness') |
| 18 | + |
| 19 | + $results = $UserRegistrationDetails | Where-Object { |
| 20 | + $userId = $_.id |
| 21 | + $matchingUser = $Users | Where-Object { $_.id -eq $userId -and $_.accountEnabled } |
| 22 | + $matchingUser |
| 23 | + } | ForEach-Object { |
| 24 | + $regDetail = $_ |
| 25 | + $matchingUser = $Users | Where-Object { $_.id -eq $regDetail.id } |
| 26 | + $hasPhishResistant = $false |
| 27 | + |
| 28 | + if ($regDetail.methodsRegistered) { |
| 29 | + foreach ($method in $PhishResistantMethods) { |
| 30 | + if ($regDetail.methodsRegistered -contains $method) { |
| 31 | + $hasPhishResistant = $true |
| 32 | + break |
| 33 | + } |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + [PSCustomObject]@{ |
| 38 | + id = $regDetail.id |
| 39 | + displayName = $regDetail.userDisplayName |
| 40 | + phishResistantAuthMethod = $hasPhishResistant |
| 41 | + lastSuccessfulSignInDateTime = $matchingUser.signInActivity.lastSuccessfulSignInDateTime |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + $totalUserCount = $results.Length |
| 46 | + $phishResistantUsers = $results | Where-Object { $_.phishResistantAuthMethod } |
| 47 | + $phishableUsers = $results | Where-Object { !$_.phishResistantAuthMethod } |
| 48 | + |
| 49 | + $phishResistantUserCount = $phishResistantUsers.Length |
| 50 | + |
| 51 | + $passed = $totalUserCount -eq $phishResistantUserCount |
| 52 | + |
| 53 | + $testResultMarkdown = if ($passed) { |
| 54 | + "Validated that all users have registered phishing resistant authentication methods.`n`n%TestResult%" |
| 55 | + } else { |
| 56 | + "Found users that have not yet registered phishing resistant authentication methods`n`n%TestResult%" |
| 57 | + } |
| 58 | + |
| 59 | + $mdInfo = "## Users strong authentication methods`n`n" |
| 60 | + |
| 61 | + if ($passed) { |
| 62 | + $mdInfo = "All users have registered phishing resistant authentication methods.`n`n" |
| 63 | + } else { |
| 64 | + $mdInfo = "Found users that have not registered phishing resistant authentication methods.`n`n" |
| 65 | + } |
| 66 | + |
| 67 | + $mdInfo = $mdInfo + "| User | Last sign in | Phishing resistant method registered |`n" |
| 68 | + $mdInfo = $mdInfo + "| :--- | :--- | :---: |`n" |
| 69 | + |
| 70 | + $userLinkFormat = 'https://entra.microsoft.com/#view/Microsoft_AAD_UsersAndTenants/UserProfileMenuBlade/~/UserAuthMethods/userId/{0}/hidePreviewBanner~/true' |
| 71 | + |
| 72 | + $mdLines = @($phishableUsers | Sort-Object displayName | ForEach-Object { |
| 73 | + $userLink = $userLinkFormat -f $_.id |
| 74 | + $lastSignInDate = if ($_.lastSuccessfulSignInDateTime) { (Get-Date $_.lastSuccessfulSignInDateTime -Format 'yyyy-MM-dd') } else { 'Never' } |
| 75 | + "|[$($_.displayName)]($userLink)| $lastSignInDate | ❌ |`n" |
| 76 | + }) |
| 77 | + $mdInfo = $mdInfo + ($mdLines -join '') |
| 78 | + |
| 79 | + $mdLines = @($phishResistantUsers | Sort-Object displayName | ForEach-Object { |
| 80 | + $userLink = $userLinkFormat -f $_.id |
| 81 | + $lastSignInDate = if ($_.lastSuccessfulSignInDateTime) { (Get-Date $_.lastSuccessfulSignInDateTime -Format 'yyyy-MM-dd') } else { 'Never' } |
| 82 | + "|[$($_.displayName)]($userLink)| $lastSignInDate | ✅ |`n" |
| 83 | + }) |
| 84 | + $mdInfo = $mdInfo + ($mdLines -join '') |
| 85 | + |
| 86 | + $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo |
| 87 | + |
| 88 | + Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21801' -TestType 'Identity' -Status $(if ($passed) { 'Passed' } else { 'Failed' }) -ResultMarkdown $testResultMarkdown -Risk 'Medium' -Name 'Users have strong authentication methods configured' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Credential Management' |
| 89 | + |
| 90 | + } catch { |
| 91 | + $ErrorMessage = Get-CippException -Exception $_ |
| 92 | + Write-LogMessage -API 'Tests' -tenant $Tenant -message "Failed to run test: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage |
| 93 | + Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21801' -TestType 'Identity' -Status 'Failed' -ResultMarkdown "Test failed: $($ErrorMessage.NormalizedError)" -Risk 'Medium' -Name 'Users have strong authentication methods configured' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Credential Management' |
| 94 | + } |
| 95 | +} |
0 commit comments