SHELL/5.1.6.1.ps1
|
$CheckId = "5.1.6.1" $Title = "Ensure that collaboration invitations are sent to allowed domains only" $Level = "L2" $BenchmarkType = "Automated" $AuditCommands = @( '$LegacyUri = "https://graph.microsoft.com/beta/legacy/policies"', '(Invoke-MgGraphRequest -Uri $LegacyUri -Method GET).value | Where-Object { $_.type -eq "B2BManagementPolicy" }', '$ModernUri = "https://graph.microsoft.com/beta/policies/b2bManagementPolicy"', 'Invoke-MgGraphRequest -Uri $ModernUri -Method GET' ) function Get-NestedValue { param( [AllowNull()][object]$InputObject, [string[]]$Path ) $Current = $InputObject foreach ($Segment in $Path) { if ($null -eq $Current) { return $null } if ($Current -is [System.Collections.IDictionary]) { if ($Current.Contains($Segment)) { $Current = $Current[$Segment] } else { return $null } } else { $Prop = $Current.PSObject.Properties[$Segment] if ($null -eq $Prop) { return $null } $Current = $Prop.Value } } return $Current } try { if (-not (Get-Command -Name Invoke-MgGraphRequest -ErrorAction SilentlyContinue)) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "ERROR" Pass = $null Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = "Invoke-MgGraphRequest cmdlet is unavailable in the current session." Timestamp = Get-Date } return } $Uri = $null $Policies = @() $LegacyUri = "https://graph.microsoft.com/beta/legacy/policies" $ModernUri = "https://graph.microsoft.com/beta/policies/b2bManagementPolicy" try { $LegacyResponse = Invoke-MgGraphRequest -Uri $LegacyUri -Method GET -ErrorAction Stop $Policies = @($LegacyResponse.value | Where-Object { [string]$_.type -eq 'B2BManagementPolicy' }) if ($Policies.Count -gt 0) { $Uri = $LegacyUri } } catch { $Policies = @() } if ($Policies.Count -eq 0) { try { $ModernResponse = Invoke-MgGraphRequest -Uri $ModernUri -Method GET -ErrorAction Stop if ($null -ne $ModernResponse) { $Policies = @([pscustomobject]@{ type = "B2BManagementPolicy" definition = $ModernResponse }) $Uri = $ModernUri } } catch { $Policies = @() } } if (@($Policies).Count -eq 0) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "FAIL" Pass = $false Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands LegacyUri = $LegacyUri ModernUri = $ModernUri SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = "No B2B management policy data was returned by either legacy or modern Graph endpoints." Timestamp = Get-Date } return } $Policy = $Policies | Select-Object -First 1 $DefinitionRaw = $Policy.definition $DefinitionParsed = $null if ($DefinitionRaw -is [string]) { $DefinitionParsed = $DefinitionRaw | ConvertFrom-Json -Depth 20 } else { $DefinitionParsed = $DefinitionRaw } $DomainsPolicy = Get-NestedValue -InputObject $DefinitionParsed -Path @('B2BManagementPolicy', 'InvitationsAllowedAndBlockedDomainsPolicy') if ($null -eq $DomainsPolicy) { $DomainsPolicy = Get-NestedValue -InputObject $DefinitionParsed -Path @('InvitationsAllowedAndBlockedDomainsPolicy') } if ($null -eq $DomainsPolicy) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "FAIL" Pass = $false Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands Uri = $Uri RawDefinition = $DefinitionRaw SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = "InvitationsAllowedAndBlockedDomainsPolicy was not found in returned B2B management policy data." Timestamp = Get-Date } return } $AllowedDomains = Get-NestedValue -InputObject $DomainsPolicy -Path @('AllowedDomains') $BlockedDomainsPresent = $false if ($DomainsPolicy -is [System.Collections.IDictionary]) { $BlockedDomainsPresent = $DomainsPolicy.Contains('BlockedDomains') } elseif ($null -ne $DomainsPolicy.PSObject.Properties['BlockedDomains']) { $BlockedDomainsPresent = $true } $AllowedDomainsList = @($AllowedDomains | ForEach-Object { ([string]$_).Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) if ($BlockedDomainsPresent) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "FAIL" Pass = $false Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands Uri = $Uri AllowedDomains = @($AllowedDomainsList) BlockedDomainsPresent = $true DomainsPolicy = $DomainsPolicy SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = "BlockedDomains is present in InvitationsAllowedAndBlockedDomainsPolicy (non-compliant per CIS)." Timestamp = Get-Date } return } if ($null -eq $AllowedDomains) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "FAIL" Pass = $false Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands Uri = $Uri DomainsPolicy = $DomainsPolicy SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = "AllowedDomains property was not found." Timestamp = Get-Date } return } $ApprovedDomains = @([string]$env:ROOT365_APPROVED_B2B_DOMAINS -split '[,; ]+' | ForEach-Object { $_.Trim().ToLowerInvariant() } | Where-Object { $_ }) if (@($AllowedDomainsList).Count -eq 0) { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "PASS" Pass = $true Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands Uri = $Uri AllowedDomains = @() BlockedDomainsPresent = $false ApprovedDomains = @($ApprovedDomains) ApprovedDomainsSource = "Environment variable ROOT365_APPROVED_B2B_DOMAINS" SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = $null Timestamp = Get-Date } return } $AllowedDomainsLower = @($AllowedDomainsList | ForEach-Object { $_.ToLowerInvariant() }) $NotApproved = @($AllowedDomainsLower | Where-Object { $_ -notin $ApprovedDomains } | Select-Object -Unique) $Pass = ($ApprovedDomains.Count -gt 0 -and $NotApproved.Count -eq 0) [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = if ($Pass) { "PASS" } else { "FAIL" } Pass = $Pass Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands Uri = $Uri AllowedDomains = @($AllowedDomainsList) AllowedDomainsLower = @($AllowedDomainsLower) ApprovedDomains = @($ApprovedDomains) NotApprovedDomains = @($NotApproved) ApprovedDomainsSource = "Environment variable ROOT365_APPROVED_B2B_DOMAINS" BlockedDomainsPresent = $false SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = if ($Pass) { $null } elseif ($ApprovedDomains.Count -eq 0) { "AllowedDomains are configured but ROOT365_APPROVED_B2B_DOMAINS is empty. Define approved domains to complete deterministic validation." } else { "AllowedDomains contains non-approved entries: $($NotApproved -join ', ')." } Timestamp = Get-Date } } catch { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "ERROR" Pass = $null Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = $_.Exception.Message Timestamp = Get-Date } } |