Private/M365Monitor/Detections/Test-M365TeamsExternalAccess.ps1
|
# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0 # https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/ # AI/LLM use: see AI-USAGE.md for required attribution function Test-M365TeamsExternalAccess { [CmdletBinding()] param( [PSCustomObject[]]$Events = @() ) $results = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($event in $Events) { $activity = $event.Activity ?? '' $targetName = $event.TargetName ?? '' $changeDetails = [System.Collections.Generic.List[string]]::new() $accessWeakened = $false foreach ($prop in $event.ModifiedProps) { $propName = $prop.Name ?? '' $newVal = $prop.NewValue ?? '' $oldVal = $prop.OldValue ?? '' # External access (federation) settings if ($propName -match 'AllowFederatedUsers|AllowTeamsConsumer|AllowPublicUsers') { $cleanNew = ($newVal -replace '"', '').Trim() $cleanOld = ($oldVal -replace '"', '').Trim() if ($cleanNew -match 'true|True' -and $cleanOld -match 'false|False') { $accessWeakened = $true $changeDetails.Add("External access enabled: $propName set to True") } elseif ($cleanNew -match 'false|False' -and $cleanOld -match 'true|True') { $changeDetails.Add("External access restricted: $propName set to False") } else { $changeDetails.Add("$propName changed: '$cleanOld' -> '$cleanNew'") } } # Guest access settings if ($propName -match 'AllowGuestUser|AllowGuestAccess|GuestAccessEnabled') { $cleanNew = ($newVal -replace '"', '').Trim() $cleanOld = ($oldVal -replace '"', '').Trim() if ($cleanNew -match 'true|True' -and $cleanOld -match 'false|False') { $accessWeakened = $true $changeDetails.Add("Guest access enabled: $propName set to True") } else { $changeDetails.Add("$propName changed: '$cleanOld' -> '$cleanNew'") } } # Domain allow/block list changes if ($propName -match 'AllowedDomains|BlockedDomains') { $cleanNew = ($newVal -replace '"', '').Trim() $cleanOld = ($oldVal -replace '"', '').Trim() if ($propName -match 'AllowedDomains' -and $cleanNew -match 'AllowAllKnownDomains|\*') { $accessWeakened = $true $changeDetails.Add("Allowed domains set to all: $propName = '$cleanNew'") } elseif ($propName -match 'BlockedDomains' -and (-not $cleanNew -or $cleanNew -eq '' -or $cleanNew -eq '[]')) { $accessWeakened = $true $changeDetails.Add("Blocked domains list cleared") } else { $changeDetails.Add("$propName changed: '$cleanOld' -> '$cleanNew'") } } # Meeting policy changes that allow external participants if ($propName -match 'AllowAnonymousUsersToJoinMeeting|AllowAnonymousUsersToStartMeeting|AutoAdmittedUsers') { $cleanNew = ($newVal -replace '"', '').Trim() if ($cleanNew -match 'true|Everyone|EveryoneInCompanyAndFederated') { $accessWeakened = $true $changeDetails.Add("Meeting policy weakened: $propName = '$cleanNew'") } else { $changeDetails.Add("$propName changed to: '$cleanNew'") } } # External app access if ($propName -match 'AllowExternalApps|AllowSideloading|AllowThirdPartyApps') { $cleanNew = ($newVal -replace '"', '').Trim() if ($cleanNew -match 'true|True') { $changeDetails.Add("External app access enabled: $propName = True") } } } # Severity assessment $severity = if ($accessWeakened) { 'Medium' } elseif ($activity -match 'guest|federation|external') { 'Low' } else { 'Low' } $description = if ($accessWeakened) { "Teams external access weakened: '$targetName' by $($event.Actor)" } else { "Teams access policy modified: '$targetName' by $($event.Actor)" } $results.Add([PSCustomObject]@{ Timestamp = $event.Timestamp Actor = $event.Actor DetectionType = 'm365TeamsExternalAccess' Description = $description Details = @{ PolicyName = $targetName Activity = $activity AccessWeakened = $accessWeakened ChangeNotes = @($changeDetails) ModifiedProps = $event.ModifiedProps } Severity = $severity }) } return @($results) } |