Private/Rules/Get-TcEntraIdRule.ps1
|
function Get-TcEntraIdRule { <# .SYNOPSIS Returns the Entra ID rules, as script blocks that take the role of one tenant. #> [CmdletBinding()] [OutputType([scriptblock])] param() { # Inactive members param($Role) if (-not (Test-TcRuleTable $Role 'users')) { return } $days = [int](Get-TcRuleThreshold 'inactiveDays') $members = @(Get-TcRuleRow $Role 'users' | Where-Object { (Get-TcProperty $_ 'Type') -eq 'Member' -and (Test-TcRowTrue $_ 'Enabled') -and $null -ne (Get-TcProperty $_ 'Days inactive') }) if (-not $members.Count) { return } $inactive = @($members | Where-Object { $d = [int](Get-TcProperty $_ 'Days inactive'); ($d -ge $days -or $d -eq -1) -and (Get-TcRowInteger $_ 'Days since creation') -ge $days }) if (-not $inactive.Count) { return } $never = @($inactive | Where-Object { [int](Get-TcProperty $_ 'Days inactive') -eq -1 }).Count $items = $inactive | ForEach-Object { $d = [int](Get-TcProperty $_ 'Days inactive'); '{0} - {1}' -f (Get-TcProperty $_ 'UPN'), $(if ($d -eq -1) { 'never signed in' } else { "$d days" }) } Add-TcFinding 'ENT-INACTIVE' $Role -Values @{ count = $inactive.Count; days = $days; members = $members.Count; never = $never } -Items $items } { # Disabled accounts that keep licenses param($Role) $rows = @(Get-TcRuleRow $Role 'users' | Where-Object { -not (Test-TcRowTrue $_ 'Enabled') -and (Get-TcRowInteger $_ 'License count') -gt 0 }) if (-not $rows.Count) { return } $licenses = (@($rows | ForEach-Object { [int](Get-TcProperty $_ 'License count') }) | Measure-Object -Sum).Sum Add-TcFinding 'ENT-DISABLED-LICENSED' $Role -Values @{ count = $rows.Count; licenses = $licenses } -Items ($rows | ForEach-Object { '{0} - {1}' -f (Get-TcProperty $_ 'UPN'), (Get-TcProperty $_ 'Licenses') }) } { # Guests param($Role) $guests = @(Get-TcRuleRow $Role 'users' | Where-Object { (Get-TcProperty $_ 'Type') -eq 'Guest' }) if (-not $guests.Count) { return } $days = [int](Get-TcRuleThreshold 'inactiveDays') $stale = @($guests | Where-Object { $d = Get-TcProperty $_ 'Days inactive' (Get-TcProperty $_ 'Invitation state') -eq 'PendingAcceptance' -or ($null -ne $d -and ([int]$d -ge $days -or [int]$d -eq -1)) }) $values = @{ count = $guests.Count; stale = $stale.Count; days = $days } if ($Role -ne 'Target') { $describe = { $d = Get-TcProperty $_ 'Days inactive' if ((Get-TcProperty $_ 'Invitation state') -eq 'PendingAcceptance') { 'invitation never accepted' } elseif ($null -eq $d) { 'activity unknown' } elseif ([int]$d -eq -1) { 'never signed in' } else { "$d days" } } $sorted = $guests | Sort-Object { $d = Get-TcProperty $_ 'Days inactive'; if ("$d" -ne '') { [int]$d } else { 99999 } } Add-TcFinding 'ENT-GUESTS' $Role -Values $values -Items ($sorted | ForEach-Object { '{0} - {1}' -f (Get-TcProperty $_ 'UPN'), (& $describe) }) } elseif ($stale.Count) { Add-TcFinding 'ENT-GUESTS' $Role -Values $values -Items ($stale | ForEach-Object { Get-TcProperty $_ 'UPN' }) } } { # Global Administrators param($Role) if (-not (Test-TcRuleTable $Role 'adminRoles')) { return } $admins = @(Get-TcRuleRow $Role 'adminRoles' | Where-Object { (Get-TcProperty $_ 'Role') -eq 'Global Administrator' -and (Get-TcProperty $_ 'Assignment') -eq 'Active' }) $external = @($admins | Where-Object { (Get-TcProperty $_ 'Principal') -match '#EXT#' }) if ($admins.Count -gt 5 -or $admins.Count -lt 2 -or $external.Count) { $severity = if ($admins.Count -gt 8 -or $external.Count) { 'High' } else { 'Medium' } $note = if ($external.Count) { Format-TcRuleText (Get-TcRulePart 'ENT-ADMINS' 'external') @{ count = $external.Count } } else { '' } Add-TcFinding 'ENT-ADMINS' $Role -Severity $severity -Values @{ count = $admins.Count; external = $note } -Items ($admins | ForEach-Object { '{0} ({1})' -f (Get-TcProperty $_ 'Principal'), (Get-TcProperty $_ 'Principal type') }) } } { # MFA for all and legacy authentication param($Role) $policies = @(Get-TcRuleRow $Role 'conditionalAccess') $defaults = Get-TcRuleSetting $Role 'securitySettings' 'Security defaults enabled' if (-not (Test-TcRuleTable $Role 'conditionalAccess') -and $null -eq $defaults) { return } $mfa = @($policies | Where-Object { Test-TcRowTrue $_ 'MFA for all' }) $legacy = @($policies | Where-Object { Test-TcRowTrue $_ 'Blocks legacy authentication' }) $reportOnly = @($policies | Where-Object { (Get-TcProperty $_ 'State') -eq 'enabledForReportingButNotEnforced' }) if (-not $mfa.Count -and -not (Test-TcTrue $defaults)) { Add-TcFinding 'ENT-MFA' $Role -Values @{ reportOnly = $reportOnly.Count } -Items ($policies | ForEach-Object { '{0} - {1} - {2}' -f (Get-TcProperty $_ 'Name'), (Get-TcProperty $_ 'State'), (Get-TcProperty $_ 'Controls') }) } if (-not $legacy.Count -and -not (Test-TcTrue $defaults)) { Add-TcFinding 'ENT-LEGACY-AUTH' $Role } } { # MFA registration param($Role) $rows = @(Get-TcRuleRow $Role 'mfaRegistrations' | Where-Object { (Get-TcProperty $_ 'Type') -eq 'member' -and -not (Test-TcRowTrue $_ 'MFA registered') }) if (-not $rows.Count) { return } $active = @{} foreach ($user in Get-TcRuleRow $Role 'users') { if ((Test-TcRowTrue $user 'Enabled') -and (Get-TcProperty $user 'Type') -eq 'Member') { $active[[string](Get-TcProperty $user 'UPN')] = 1 } } if ($active.Count) { $rows = @($rows | Where-Object { $active.ContainsKey([string](Get-TcProperty $_ 'UPN')) }) } if (-not $rows.Count) { return } Add-TcFinding 'ENT-MFA-REGISTRATION' $Role -Values @{ count = $rows.Count } -Items ($rows | ForEach-Object { Get-TcProperty $_ 'UPN' }) } { # Group creation in the target param($Role) if ($Role -ne 'Target' -or -not (Test-TcRuleTable $Role 'groupSettings')) { return } $value = Get-TcRuleSetting $Role 'groupSettings' 'Group.Unified.EnableGroupCreation' if ($null -eq $value -or (Test-TcTrue $value)) { Add-TcFinding 'ENT-GROUP-CREATION' $Role } } { # User permissions param($Role) $invitations = Get-TcRuleSetting $Role 'securitySettings' 'Invitations allowed from' $apps = Get-TcRuleSetting $Role 'securitySettings' 'User permissions.allowedToCreateApps' $details = @() if ($invitations -eq 'everyone') { $details += Get-TcRulePart 'ENT-AUTHORIZATION' 'anyoneInvites' } if (Test-TcTrue $apps) { $details += Get-TcRulePart 'ENT-AUTHORIZATION' 'usersRegisterApps' } if ($details.Count) { Add-TcFinding 'ENT-AUTHORIZATION' $Role -Values @{ details = $details -join '; ' } } } { # Secrets and certificates param($Role) $limit = [int](Get-TcRuleThreshold 'secretExpiryDays') $apps = @(Get-TcRuleRow $Role 'applications' | Where-Object { $d = Get-TcProperty $_ 'Days to expiry' (Get-TcRowInteger $_ 'Expired') -gt 0 -or ($null -ne $d -and "$d" -ne '' -and [int]$d -le $limit) }) if (-not $apps.Count) { return } Add-TcFinding 'ENT-SECRETS' $Role -Values @{ count = $apps.Count; days = $limit } -Items ($apps | ForEach-Object { $d = Get-TcProperty $_ 'Days to expiry' '{0} - expired {1} - next expiry {2}' -f (Get-TcProperty $_ 'Name'), (Get-TcProperty $_ 'Expired'), $(if ("$d") { "in $d days" } else { 'none' }) }) } { # Applications that do not follow users param($Role) if ($Role -eq 'Target') { return } $third = @(Get-TcRuleRow $Role 'servicePrincipals' | Where-Object { (Get-TcProperty $_ 'Origin') -eq 'Third party' -and (Test-TcRowTrue $_ 'Enabled') }) $consented = @(Get-TcRuleRow $Role 'delegatedConsents' | Where-Object { (Get-TcProperty $_ 'Consent type') -like 'Administrator*' } | ForEach-Object { Get-TcProperty $_ 'Application' } | Sort-Object -Unique) if ($third.Count) { Add-TcFinding 'ENT-THIRD-PARTY-APPS' $Role -Values @{ count = $third.Count; consented = $consented.Count } -Items ($third | Sort-Object { Get-TcProperty $_ 'Name' } | ForEach-Object { $publisher = Get-TcProperty $_ 'Publisher' '{0}{1}{2}' -f (Get-TcProperty $_ 'Name'), $(if ($publisher) { " - $publisher" } else { '' }), $(if ($consented -contains (Get-TcProperty $_ 'Name')) { ' - admin consent' } else { '' }) }) } $own = @(Get-TcRuleRow $Role 'applications') if ($own.Count) { Add-TcFinding 'ENT-APP-REGISTRATIONS' $Role -Values @{ count = $own.Count } -Items ($own | ForEach-Object { '{0} - {1} - created {2}' -f (Get-TcProperty $_ 'Name'), (Get-TcProperty $_ 'Audience'), (Get-TcProperty $_ 'Created') }) } } { # Directory synchronization param($Role) $sync = Get-TcRuleSetting $Role 'organizationSettings' 'AD sync enabled' $days = Get-TcRuleSetting $Role 'organizationSettings' 'Days since last sync' if ($Role -ne 'Target' -and (Test-TcTrue $sync)) { Add-TcFinding 'ENT-SYNC-SOURCE' $Role -Values @{ days = $days } } if ($Role -eq 'Target' -and (Test-TcTrue $sync) -and $null -ne $days -and [int]$days -ge 1) { Add-TcFinding 'ENT-SYNC-TARGET' $Role -Values @{ days = $days } } } { # License errors and stale devices param($Role) $errors = @(Get-TcRuleRow $Role 'users' | Where-Object { (Get-TcRowInteger $_ 'License errors') -gt 0 }) if ($errors.Count) { Add-TcFinding 'ENT-LICENSE-ERRORS' $Role -Values @{ count = $errors.Count } -Items ($errors | ForEach-Object { Get-TcProperty $_ 'UPN' }) } $stale = @(Get-TcRuleRow $Role 'devices' | Where-Object { $d = Get-TcProperty $_ 'Days inactive'; $null -ne $d -and "$d" -ne '' -and [int]$d -ge 90 }) if ($stale.Count -ge 5) { Add-TcFinding 'ENT-STALE-DEVICES' $Role -Values @{ count = $stale.Count } -Items ($stale | ForEach-Object { '{0} - {1} - {2} days' -f (Get-TcProperty $_ 'Name'), (Get-TcProperty $_ 'OS'), (Get-TcProperty $_ 'Days inactive') }) } } { # Business plan cap of the target param($Role) if ($Role -ne 'Target') { return } $capped = @(Get-TcRuleRow $Role 'licenses' | Where-Object { (Get-TcProperty $_ 'SKU') -match '^(SPB|O365_BUSINESS_PREMIUM|O365_BUSINESS_ESSENTIALS|O365_BUSINESS|SMB_BUSINESS.*)$' -and [int](Get-TcProperty $_ 'Purchased') -ge 290 }) if ($capped.Count) { $plans = @($capped | ForEach-Object { '{0}: {1} purchased, {2} free' -f (Get-TcProperty $_ 'SKU'), (Get-TcProperty $_ 'Purchased'), (Get-TcProperty $_ 'Available') }) -join '; ' Add-TcFinding 'ENT-BUSINESS-CAP' $Role -Values @{ plans = $plans } } } { # Legacy sign-ins and sign-in countries param($Role) $legacy = @(Get-TcRuleRow $Role 'legacySignIns') if ($legacy.Count) { $accounts = @($legacy | ForEach-Object { Get-TcProperty $_ 'UPN' } | Sort-Object -Unique) $clients = @($legacy | Group-Object { Get-TcProperty $_ 'Client' } | Sort-Object Count -Descending | ForEach-Object { '{0} ({1})' -f $_.Name, $_.Count }) -join ', ' Add-TcFinding 'ENT-LEGACY-SIGN-INS' $Role -Values @{ count = $accounts.Count; clients = $clients } -Items ($legacy | Sort-Object { - [int](Get-TcProperty $_ 'Sign-ins') } | ForEach-Object { '{0} - {1} - {2} sign-in(s)' -f (Get-TcProperty $_ 'UPN'), (Get-TcProperty $_ 'Client'), (Get-TcProperty $_ 'Sign-ins') }) } $homeCountries = @($script:TcRuleRun['HomeCountry']) if (-not $homeCountries.Count) { $homeCountries = @(Get-TcRuleSetting $Role 'organizationSettings' 'Country' | Where-Object { $_ }) } $countries = @(Get-TcRuleRow $Role 'signInSummary' | Where-Object { (Get-TcProperty $_ 'Dimension') -eq 'Country' -and (Get-TcProperty $_ 'Value') -notin ($homeCountries + '(empty)') }) if ($countries.Count) { Add-TcFinding 'ENT-COUNTRIES' $Role -Values @{ count = $countries.Count; home = $homeCountries -join ', ' } -Items ($countries | Sort-Object { - [int](Get-TcProperty $_ 'Sign-ins') } | ForEach-Object { '{0} - {1} sign-in(s) - {2} user(s)' -f (Get-TcProperty $_ 'Value'), (Get-TcProperty $_ 'Sign-ins'), (Get-TcProperty $_ 'Users') }) } } { # Broad application permissions param($Role) $sensitive = '(?i)(ReadWrite\.All|FullControl|full_access_as_app|Mail\.Send|Mail\.Read|Directory\.ReadWrite|RoleManagement|Application\.ReadWrite|Sites\.Read\.All|Files\.Read\.All|User\.ReadWrite|Exchange\.ManageAsApp)' $rows = @(Get-TcRuleRow $Role 'applicationPermissions' | Where-Object { (Get-TcProperty $_ 'Permission') -match $sensitive }) if (-not $rows.Count) { return } $apps = @($rows | ForEach-Object { Get-TcProperty $_ 'Application' } | Sort-Object -Unique) Add-TcFinding 'ENT-APP-PERMISSIONS' $Role -Values @{ count = $apps.Count; permissions = $rows.Count } -Items ($rows | ForEach-Object { '{0} - {1} - {2}' -f (Get-TcProperty $_ 'Application'), (Get-TcProperty $_ 'Resource'), (Get-TcProperty $_ 'Permission') }) } { # Risky accounts param($Role) $rows = @(Get-TcRuleRow $Role 'riskyUsers') if ($rows.Count) { Add-TcFinding 'ENT-RISK' $Role -Values @{ count = $rows.Count } -Items ($rows | ForEach-Object { '{0} - {1} - {2}' -f (Get-TcProperty $_ 'UPN'), (Get-TcProperty $_ 'Level'), (Get-TcProperty $_ 'State') }) } } { # Synchronization errors param($Role) $rows = @(Get-TcRuleRow $Role 'users' | Where-Object { (Get-TcRowInteger $_ 'Sync errors') -gt 0 }) if ($rows.Count) { Add-TcFinding 'ENT-SYNC-ERRORS' $Role -Values @{ count = $rows.Count } -Items ($rows | ForEach-Object { Get-TcProperty $_ 'UPN' }) } } } |