Public/Get-IOCrossTenantAccessReport.ps1
|
function Get-IOCrossTenantAccessReport { <# .SYNOPSIS Summarizes cross-tenant access policy settings and flags overly permissive configurations. .EXAMPLE Get-IOCrossTenantAccessReport .EXAMPLE Get-IOCrossTenantAccessReport -ToCsv "cross-tenant.csv" #> [CmdletBinding()] param( [string]$ToCsv ) $cmdName = $MyInvocation.MyCommand.Name Write-IOLog 'Analyzing cross-tenant access policies...' -Level Info -Component $cmdName $results = [System.Collections.Generic.List[PSCustomObject]]::new() # ── Default policy ───────────────────────────────────────────────────── try { $defaultPolicy = Invoke-IOGraphRequest -Uri 'v1.0/policies/crossTenantAccessPolicy/default' -SingleResult -NoPagination } catch { Write-IOLog "Could not retrieve cross-tenant access policy. Ensure Policy.Read.All scope is granted." -Level Warning -Component $cmdName return } if ($defaultPolicy -and $defaultPolicy.Count -gt 0) { $dp = $defaultPolicy[0] $inboundTrust = 'Not Configured' if ($dp.inboundTrust) { $trustParts = @() if ($dp.inboundTrust.isMfaAccepted) { $trustParts += 'MFA' } if ($dp.inboundTrust.isCompliantDeviceAccepted) { $trustParts += 'CompliantDevice' } if ($dp.inboundTrust.isHybridAzureADJoinedDeviceAccepted) { $trustParts += 'HybridJoined' } $inboundTrust = if ($trustParts.Count -gt 0) { $trustParts -join '; ' } else { 'None' } } $risks = [System.Collections.Generic.List[string]]::new() $b2bIn = if ($dp.PSObject.Properties['b2bCollaborationInbound']) { $dp.b2bCollaborationInbound } else { $null } $b2bDC = if ($dp.PSObject.Properties['b2bDirectConnectInbound']) { $dp.b2bDirectConnectInbound } else { $null } if ($b2bIn -and $b2bIn.PSObject.Properties['applications'] -and $b2bIn.applications.accessType -eq 'allowed' -and $b2bIn.applications.PSObject.Properties['targets'] -and $b2bIn.applications.targets.target -contains 'AllApplications') { $risks.Add('InboundB2B_AllApps') } if ($b2bDC -and $b2bDC.PSObject.Properties['applications'] -and $b2bDC.applications.accessType -eq 'allowed' -and $b2bDC.applications.PSObject.Properties['targets'] -and $b2bDC.applications.targets.target -contains 'AllApplications') { $risks.Add('InboundDirectConnect_AllApps') } $results.Add([PSCustomObject]@{ PartnerTenant = 'DEFAULT (all tenants)' PartnerTenantId = '*' Direction = 'Default Policy' B2BInbound = if ($b2bIn) { 'Configured' } else { 'Not Configured' } B2BOutbound = if ($dp.PSObject.Properties['b2bCollaborationOutbound'] -and $dp.b2bCollaborationOutbound) { 'Configured' } else { 'Not Configured' } DirectConnect = if ($b2bDC) { 'Configured' } else { 'Not Configured' } InboundTrust = $inboundTrust Risks = if ($risks.Count -gt 0) { $risks -join '; ' } else { 'None' } RiskCount = $risks.Count }) } # ── Partner-specific policies ────────────────────────────────────────── try { $partners = Invoke-IOGraphRequest -Uri 'v1.0/policies/crossTenantAccessPolicy/partners' } catch { $partners = @() } foreach ($partner in $partners) { $risks = [System.Collections.Generic.List[string]]::new() $inboundTrust = 'Not Configured' if ($partner.inboundTrust) { $trustParts = @() if ($partner.inboundTrust.isMfaAccepted) { $trustParts += 'MFA' } if ($partner.inboundTrust.isCompliantDeviceAccepted) { $trustParts += 'CompliantDevice' } if ($partner.inboundTrust.isHybridAzureADJoinedDeviceAccepted) { $trustParts += 'HybridJoined' } $inboundTrust = if ($trustParts.Count -gt 0) { $trustParts -join '; ' } else { 'None' } } # Check for overly permissive inbound $pB2bIn = if ($partner.PSObject.Properties['b2bCollaborationInbound']) { $partner.b2bCollaborationInbound } else { $null } if ($pB2bIn -and $pB2bIn.PSObject.Properties['applications'] -and $pB2bIn.applications.accessType -eq 'allowed') { if ($pB2bIn.applications.PSObject.Properties['targets'] -and $pB2bIn.applications.targets.target -contains 'AllApplications') { $risks.Add('B2BInbound_AllApps') } } # Check inbound users scope if ($pB2bIn -and $pB2bIn.PSObject.Properties['usersAndGroups'] -and $pB2bIn.usersAndGroups.accessType -eq 'allowed') { if ($pB2bIn.usersAndGroups.PSObject.Properties['targets'] -and $pB2bIn.usersAndGroups.targets.target -contains 'AllUsers') { $risks.Add('B2BInbound_AllUsers') } } $tenantName = $partner.tenantId # Try to resolve tenant name if ($partner.PSObject.Properties['identityPartner'] -and $partner.identityPartner) { $tenantName = "$($partner.identityPartner) ($($partner.tenantId))" } $results.Add([PSCustomObject]@{ PartnerTenant = $tenantName PartnerTenantId = $partner.tenantId Direction = 'Partner-Specific' B2BInbound = if ($pB2bIn) { 'Configured' } else { 'Inherited' } B2BOutbound = if ($partner.PSObject.Properties['b2bCollaborationOutbound'] -and $partner.b2bCollaborationOutbound) { 'Configured' } else { 'Inherited' } DirectConnect = if ($partner.PSObject.Properties['b2bDirectConnectInbound'] -and $partner.b2bDirectConnectInbound) { 'Configured' } else { 'Inherited' } InboundTrust = $inboundTrust Risks = if ($risks.Count -gt 0) { $risks -join '; ' } else { 'None' } RiskCount = $risks.Count }) } $sorted = $results | Sort-Object RiskCount -Descending Export-IOResult -Data $sorted -ToCsv $ToCsv -CommandName $cmdName } |