Workloads/Get-TeamsData.ps1

# Get-TeamsData.ps1
# Collects Teams, channels, guest access, and external access signals.
# Part of the M365-QuickAssess module -- not exported.

function Get-TeamsData
{
    param
    (
        $Assessment
    )

    Write-Log "Collecting Teams data"

    # -------------------------------------------------------------------
    # Teams and Channels
    # -------------------------------------------------------------------
    try
    {
        $teams      = Get-MgGroup -All `
            -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" `
            -Property Id,DisplayName `
            -ErrorAction Stop

        $teamCount    = $teams.Count
        $privateCount = 0
        $sharedCount  = 0
        $checked      = 0

        foreach ( $team in $teams )
        {
            $checked++

            try
            {
                $channels = Invoke-MgGraphRequest `
                    -Method GET `
                    -Uri    "https://graph.microsoft.com/v1.0/teams/$( $team.Id )/channels" `
                    -ErrorAction Stop

                foreach ( $ch in $channels.value )
                {
                    switch ( $ch.membershipType )
                    {
                        "private" { $privateCount++ }
                        "shared"  { $sharedCount++  }
                    }
                }
            }
            catch
            {
                Write-Log "Could not retrieve channels for team '$( $team.DisplayName )'" "WARN"
            }

            if ( $checked % 50 -eq 0 )
            {
                Write-Log "Teams: processed $checked / $teamCount"
            }
        }

        $Assessment.Teams.TeamCount           = $teamCount
        $Assessment.Teams.PrivateChannelCount = $privateCount
        $Assessment.Teams.HasPrivateChannels  = ( $privateCount -gt 0 )
        $Assessment.Teams.SharedChannelCount  = $sharedCount
        $Assessment.Teams.HasSharedChannels   = ( $sharedCount -gt 0 )
        $Assessment.Summary.TeamCount         = $teamCount

        Write-Log "Teams: Total=$teamCount PrivateChannels=$privateCount SharedChannels=$sharedCount"

        # -------------------------------------------------------------------
        # Finding: Private channels
        # -------------------------------------------------------------------
        if ( $privateCount -gt 0 )
        {
            $Assessment.Findings += New-Finding `
                -Type           "PrivateChannels" `
                -Summary        "$privateCount private channels detected across $teamCount teams" `
                -Category       "Teams" `
                -Severity       "Medium" `
                -Impact         "Private channels have their own SharePoint site collections and separate membership. They require additional attention during migration." `
                -Recommendation "Inventory private channels and validate membership and content migration separately."
        }

        # -------------------------------------------------------------------
        # Finding: Shared channels
        # -------------------------------------------------------------------
        if ( $sharedCount -gt 0 )
        {
            $Assessment.Findings += New-Finding `
                -Type           "SharedChannels" `
                -Summary        "$sharedCount shared channels detected across $teamCount teams" `
                -Category       "Teams" `
                -Severity       "Medium" `
                -Impact         "Shared channels involve external tenant participants and may have cross-tenant dependencies that complicate migration." `
                -Recommendation "Review shared channel memberships and notify external participants of the migration timeline."
        }

        # -------------------------------------------------------------------
        # Finding: Large team count
        # -------------------------------------------------------------------
        if ( $teamCount -gt 500 )
        {
            $Assessment.Findings += New-Finding `
                -Type           "LargeTeamCount" `
                -Summary        "$teamCount Teams detected" `
                -Category       "Teams" `
                -Severity       "Medium" `
                -Impact         "A large number of Teams increases migration complexity and time." `
                -Recommendation "Review Teams inventory and archive or delete inactive teams before migration."
        }
    }
    catch
    {
        Write-Log "Teams collection failed: $( $_.Exception.Message )" "ERROR"
    }

    # -------------------------------------------------------------------
    # Guest Access Signal
    # -------------------------------------------------------------------
    try
    {
        $authPolicy = Invoke-MgGraphRequest `
            -Method GET `
            -Uri    "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" `
            -ErrorAction Stop

        $hasGuestAccess = $authPolicy.allowInvitesFrom -ne "none"

        $Assessment.Teams.HasTeamsGuestAccess = $hasGuestAccess

        Write-Log "Teams Guest Access: $hasGuestAccess (allowInvitesFrom=$( $authPolicy.allowInvitesFrom ))"

        if ( $hasGuestAccess )
        {
            $Assessment.Findings += New-Finding `
                -Type           "TeamsGuestAccessEnabled" `
                -Summary        "Teams guest access is enabled" `
                -Category       "Teams" `
                -Severity       "Info" `
                -Impact         "Guest users in Teams do not migrate with standard tooling and will lose access post-migration if not re-invited in the target tenant." `
                -Recommendation "Inventory Teams guest members and plan re-invitation in the target tenant."
        }
    }
    catch
    {
        Write-Log "Guest access signal failed: $( $_.Exception.Message )" "WARN"
        $Assessment.Teams.HasTeamsGuestAccess = $null
    }

    # -------------------------------------------------------------------
    # External Access (Federation) -- best effort via Graph
    # -------------------------------------------------------------------
    try
    {
        $crossTenantPolicy = Invoke-MgGraphRequest `
            -Method GET `
            -Uri    "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy" `
            -ErrorAction Stop

        $hasExternalAccess = $null -ne $crossTenantPolicy

        $Assessment.Teams.HasTeamsExternalAccess = $hasExternalAccess

        Write-Log "Teams External Access (cross-tenant policy): $hasExternalAccess"
    }
    catch
    {
        Write-Log "External access signal failed: $( $_.Exception.Message )" "WARN"
        $Assessment.Teams.HasTeamsExternalAccess = $null
    }

    # -------------------------------------------------------------------
    # Teams Apps signal
    # -------------------------------------------------------------------
    $Assessment.Teams.HasTeamsApps = $true
}