Private/Entra/Checks/Invoke-M365TeamsChecks.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 Invoke-M365TeamsChecks { [CmdletBinding()] param( [Parameter(Mandatory)] [hashtable]$AuditData ) $checkDefs = Get-AuditCategoryDefinitions -Category 'M365TeamsChecks' $findings = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($check in $checkDefs.checks) { $funcName = "Test-Infiltration$($check.id -replace '-', '')" if (Get-Command $funcName -ErrorAction SilentlyContinue) { try { $finding = & $funcName -AuditData $AuditData -CheckDefinition $check if ($finding) { $findings.Add($finding) } } catch { $findings.Add((New-AuditFinding -CheckDefinition $check -Status 'ERROR' ` -CurrentValue "Check failed: $_")) } } else { $findings.Add((New-AuditFinding -CheckDefinition $check -Status 'SKIP' ` -CurrentValue 'Check not yet implemented')) } } return @($findings) } # ── M365TEAMS-001: External Access (Federation) ───────────────────── function Test-InfiltrationM365TEAMS001 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.ExternalAccessConfig) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams external access data not available (Teams admin module not connected)' } $config = $teams.ExternalAccessConfig $allowFederated = $config.AllowFederatedUsers $allowTeamsConsumer = $config.AllowTeamsConsumer $allowPublicUsers = $config.AllowPublicUsers $allowedDomains = $config.AllowedDomains $blockedDomains = $config.BlockedDomains # Determine if federation is restricted to specific domains $hasAllowList = ($allowedDomains -and $allowedDomains.Count -gt 0) $hasBlockList = ($blockedDomains -and $blockedDomains.Count -gt 0) $openCount = 0 if ($allowFederated -eq $true) { $openCount++ } if ($allowTeamsConsumer -eq $true) { $openCount++ } if ($allowPublicUsers -eq $true) { $openCount++ } $status = if ($openCount -eq 0) { 'PASS' } elseif ($allowFederated -eq $true -and $hasAllowList) { 'PASS' } elseif ($openCount -le 1 -and $hasBlockList) { 'WARN' } else { 'FAIL' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue "External access: Federated=$allowFederated, Consumer=$allowTeamsConsumer, Public=$allowPublicUsers. Domain restrictions: AllowList=$hasAllowList, BlockList=$hasBlockList" ` -Details @{ AllowFederatedUsers = $allowFederated AllowTeamsConsumer = $allowTeamsConsumer AllowPublicUsers = $allowPublicUsers HasAllowedDomainList = $hasAllowList HasBlockedDomainList = $hasBlockList AllowedDomainCount = if ($allowedDomains) { $allowedDomains.Count } else { 0 } BlockedDomainCount = if ($blockedDomains) { $blockedDomains.Count } else { 0 } OpenChannelCount = $openCount } } # ── M365TEAMS-002: Guest Access ────────────────────────────────────── function Test-InfiltrationM365TEAMS002 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.GuestConfig) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams guest configuration data not available (Teams admin module not connected)' } $config = $teams.GuestConfig $allowGuestAccess = $config.AllowGuestUser # Check if guest capabilities are restricted when access is enabled $guestCallingEnabled = $config.AllowIPVideo ?? $true $guestScreenSharing = $config.ScreenSharingMode ?? 'EntireScreen' $allowBox = $config.AllowBox ?? $false $allowDropBox = $config.AllowDropBox ?? $false $allowGoogleDrive = $config.AllowGoogleDrive ?? $false $thirdPartyStorageEnabled = ($allowBox -eq $true -or $allowDropBox -eq $true -or $allowGoogleDrive -eq $true) $status = if ($allowGuestAccess -eq $false) { 'PASS' } elseif ($allowGuestAccess -eq $true -and -not $thirdPartyStorageEnabled) { 'WARN' } elseif ($allowGuestAccess -eq $true -and $thirdPartyStorageEnabled) { 'FAIL' } else { 'PASS' } $description = if ($allowGuestAccess -eq $false) { 'Guest access is disabled in Teams' } elseif ($thirdPartyStorageEnabled) { 'Guest access enabled with third-party cloud storage allowed — consider restricting capabilities' } else { 'Guest access enabled with restricted third-party storage' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue $description ` -Details @{ AllowGuestUser = $allowGuestAccess AllowIPVideo = $guestCallingEnabled ScreenSharingMode = $guestScreenSharing AllowBox = $allowBox AllowDropBox = $allowDropBox AllowGoogleDrive = $allowGoogleDrive ThirdPartyStorageEnabled = $thirdPartyStorageEnabled } } # ── M365TEAMS-003: External Meeting Participants ───────────────────── function Test-InfiltrationM365TEAMS003 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.MeetingPolicies) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams meeting policy data not available (Teams admin module not connected)' } $policies = $teams.MeetingPolicies $globalPolicy = $policies | Where-Object { $_.Identity -eq 'Global' -or $_.Identity -match 'Tag:Global' } | Select-Object -First 1 if (-not $globalPolicy) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Global meeting policy not found in Teams configuration' } $allowExternal = $globalPolicy.AllowExternalParticipantGiveRequestControl $autoAdmit = $globalPolicy.AutoAdmittedUsers $status = if ($allowExternal -eq $false -and $autoAdmit -eq 'EveryoneInCompany') { 'PASS' } elseif ($allowExternal -eq $false) { 'PASS' } elseif ($autoAdmit -eq 'Everyone') { 'FAIL' } else { 'WARN' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue "External participant control: GiveRequestControl=$allowExternal, AutoAdmit=$autoAdmit" ` -Details @{ AllowExternalParticipantGiveRequestControl = $allowExternal AutoAdmittedUsers = $autoAdmit PolicyCount = $policies.Count } } # ── M365TEAMS-004: Anonymous Meeting Join ──────────────────────────── function Test-InfiltrationM365TEAMS004 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.MeetingPolicies) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams meeting policy data not available (Teams admin module not connected)' } $policies = $teams.MeetingPolicies $globalPolicy = $policies | Where-Object { $_.Identity -eq 'Global' -or $_.Identity -match 'Tag:Global' } | Select-Object -First 1 if (-not $globalPolicy) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Global meeting policy not found in Teams configuration' } $anonymousJoin = $globalPolicy.AllowAnonymousUsersToJoinMeeting $dialInBypass = $globalPolicy.AllowPSTNUsersToBypassLobby $status = if ($anonymousJoin -eq $false) { 'PASS' } elseif ($anonymousJoin -eq $true -and $dialInBypass -eq $true) { 'FAIL' } elseif ($anonymousJoin -eq $true) { 'WARN' } else { 'PASS' } $description = if ($anonymousJoin -eq $false) { 'Anonymous users cannot join meetings (recommended)' } elseif ($anonymousJoin -eq $true -and $dialInBypass -eq $true) { 'Anonymous join AND PSTN lobby bypass are both enabled — high risk' } else { 'Anonymous users can join meetings — consider disabling for sensitive environments' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue $description ` -Details @{ AllowAnonymousUsersToJoinMeeting = $anonymousJoin AllowPSTNUsersToBypassLobby = $dialInBypass } } # ── M365TEAMS-005: Recording and Transcription ────────────────────── function Test-InfiltrationM365TEAMS005 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.MeetingPolicies) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams meeting policy data not available (Teams admin module not connected)' } $policies = $teams.MeetingPolicies $globalPolicy = $policies | Where-Object { $_.Identity -eq 'Global' -or $_.Identity -match 'Tag:Global' } | Select-Object -First 1 if (-not $globalPolicy) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Global meeting policy not found in Teams configuration' } $allowRecording = $globalPolicy.AllowCloudRecording ?? 'Unknown' $allowTranscription = $globalPolicy.AllowTranscription ?? 'Unknown' $recordingStorageMode = $globalPolicy.RecordingStorageMode ?? 'Unknown' # Recording should be intentionally configured — not necessarily disabled $status = if ($allowRecording -eq $true -and $recordingStorageMode -eq 'OneDriveForBusiness') { 'PASS' } elseif ($allowRecording -eq $false) { 'PASS' } elseif ($allowRecording -eq $true) { 'WARN' } else { 'PASS' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue "Cloud recording: $allowRecording, Transcription: $allowTranscription, Storage: $recordingStorageMode" ` -Details @{ AllowCloudRecording = $allowRecording AllowTranscription = $allowTranscription RecordingStorageMode = $recordingStorageMode } } # ── M365TEAMS-006: Messaging Policies ──────────────────────────────── function Test-InfiltrationM365TEAMS006 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.MessagingPolicies) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams messaging policy data not available (Teams admin module not connected)' } $policies = $teams.MessagingPolicies if ($policies.Count -eq 0) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'WARN' ` -CurrentValue 'No Teams messaging policies found' } $globalPolicy = $policies | Where-Object { $_.Identity -eq 'Global' -or $_.Identity -match 'Tag:Global' } | Select-Object -First 1 # Check external messaging settings $allowUrlPreviews = $globalPolicy.AllowUrlPreviews ?? $true $allowUserChat = $globalPolicy.AllowUserChat ?? $true $chatPermissionRole = $globalPolicy.ChatPermissionRole ?? 'Unknown' $status = if ($chatPermissionRole -eq 'Restricted') { 'PASS' } elseif ($chatPermissionRole -eq 'Full') { 'WARN' } else { 'PASS' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue "$($policies.Count) messaging policies. Global: UrlPreviews=$allowUrlPreviews, UserChat=$allowUserChat, ChatRole=$chatPermissionRole" ` -Details @{ PolicyCount = $policies.Count AllowUrlPreviews = $allowUrlPreviews AllowUserChat = $allowUserChat ChatPermissionRole = $chatPermissionRole Policies = @($policies | ForEach-Object { @{ Identity = $_.Identity AllowUrlPreviews = $_.AllowUrlPreviews AllowOwnerDeleteMessage = $_.AllowOwnerDeleteMessage AllowUserDeleteMessage = $_.AllowUserDeleteMessage AllowUserEditMessage = $_.AllowUserEditMessage ChatPermissionRole = $_.ChatPermissionRole } }) } } # ── M365TEAMS-007: App Permission Policies ─────────────────────────── function Test-InfiltrationM365TEAMS007 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.AppPermissionPolicies) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams app permission policy data not available (Teams admin module not connected)' } $policies = $teams.AppPermissionPolicies if ($policies.Count -eq 0) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'WARN' ` -CurrentValue 'No Teams app permission policies found' } $globalPolicy = $policies | Where-Object { $_.Identity -eq 'Global' -or $_.Identity -match 'Tag:Global' } | Select-Object -First 1 $thirdPartyApps = $globalPolicy.DefaultCatalogAppsType ?? 'Unknown' $customApps = $globalPolicy.GlobalCatalogAppsType ?? 'Unknown' $privateApps = $globalPolicy.PrivateCatalogAppsType ?? 'Unknown' # AllowedAppTypes / blocked checks $allAllowed = ($thirdPartyApps -eq 'AllowedAppList' -or $thirdPartyApps -eq 'BlockedAppList') $thirdPartyBlocked = ($thirdPartyApps -eq 'BlockAllApps') $status = if ($thirdPartyBlocked) { 'PASS' } elseif ($thirdPartyApps -eq 'AllowedAppList') { 'PASS' } elseif ($thirdPartyApps -eq 'BlockedAppList') { 'WARN' } else { 'FAIL' } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue "App permissions: ThirdParty=$thirdPartyApps, Custom=$customApps, Private=$privateApps" ` -Details @{ PolicyCount = $policies.Count DefaultCatalogAppsType = $thirdPartyApps GlobalCatalogAppsType = $customApps PrivateCatalogAppsType = $privateApps ThirdPartyAppsBlocked = $thirdPartyBlocked } } # ── M365TEAMS-008: File Sharing Settings ───────────────────────────── function Test-InfiltrationM365TEAMS008 { [CmdletBinding()] param([hashtable]$AuditData, [hashtable]$CheckDefinition) $teams = $AuditData.M365Services.Teams if (-not $teams -or -not $teams.GuestConfig) { return New-AuditFinding -CheckDefinition $CheckDefinition -Status 'SKIP' ` -CurrentValue 'Teams client configuration / guest config data not available (Teams admin module not connected)' } $config = $teams.GuestConfig # Check cloud storage integrations $allowBox = $config.AllowBox ?? $false $allowDropBox = $config.AllowDropBox ?? $false $allowGoogleDrive = $config.AllowGoogleDrive ?? $false $allowShareFile = $config.AllowShareFile ?? $false $allowEgnyte = $config.AllowEgnyte ?? $false $enabledProviders = [System.Collections.Generic.List[string]]::new() if ($allowBox -eq $true) { $enabledProviders.Add('Box') } if ($allowDropBox -eq $true) { $enabledProviders.Add('Dropbox') } if ($allowGoogleDrive -eq $true) { $enabledProviders.Add('Google Drive') } if ($allowShareFile -eq $true) { $enabledProviders.Add('ShareFile') } if ($allowEgnyte -eq $true) { $enabledProviders.Add('Egnyte') } $status = if ($enabledProviders.Count -eq 0) { 'PASS' } elseif ($enabledProviders.Count -le 2) { 'WARN' } else { 'FAIL' } $description = if ($enabledProviders.Count -eq 0) { 'No third-party cloud storage providers enabled — file sharing restricted to SharePoint/OneDrive' } else { "$($enabledProviders.Count) third-party storage provider(s) enabled: $($enabledProviders -join ', ')" } return New-AuditFinding -CheckDefinition $CheckDefinition -Status $status ` -CurrentValue $description ` -Details @{ AllowBox = $allowBox AllowDropBox = $allowDropBox AllowGoogleDrive = $allowGoogleDrive AllowShareFile = $allowShareFile AllowEgnyte = $allowEgnyte EnabledProviderCount = $enabledProviders.Count EnabledProviders = @($enabledProviders) } } |