Private/Entra/Core/Get-M365ServiceData.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 Get-M365ServiceData {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$AccessToken,

        [hashtable]$ModuleAvailability,

        [switch]$Quiet
    )

    $data = @{
        Exchange     = @{}
        SharePoint   = @{}
        Teams        = @{}
        Defender     = @{}
        Audit        = @{}
        PowerPlatform = @{}
        Errors       = @{}
    }

    $hasEXO = $ModuleAvailability -and $ModuleAvailability.ExchangeOnlineManagement

    # ── Exchange Online ───────────────────────────────────────────────────
    if ($hasEXO) {
        if (-not $Quiet) {
            Write-ProgressLine -Phase INFILTRATE -Message 'Collecting Exchange Online configuration (EXO module)'
        }
        try {
            # Verify EXO connection
            $exoConnected = Get-Command Get-OrganizationConfig -ErrorAction SilentlyContinue
            if ($exoConnected) {
                $data.Exchange.OrganizationConfig = Get-OrganizationConfig -ErrorAction SilentlyContinue
                $data.Exchange.AntiSpamPolicies = @(Get-HostedContentFilterPolicy -ErrorAction SilentlyContinue)
                $data.Exchange.AntiPhishPolicies = @(Get-AntiPhishPolicy -ErrorAction SilentlyContinue)
                $data.Exchange.MalwarePolicies = @(Get-MalwareFilterPolicy -ErrorAction SilentlyContinue)
                $data.Exchange.SafeAttachmentPolicies = @(Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue)
                $data.Exchange.SafeLinksPolicies = @(Get-SafeLinksPolicy -ErrorAction SilentlyContinue)
                $data.Exchange.TransportRules = @(Get-TransportRule -ErrorAction SilentlyContinue)
                $data.Exchange.RemoteDomains = @(Get-RemoteDomain -ErrorAction SilentlyContinue)
                $data.Exchange.DkimSigningConfig = @(Get-DkimSigningConfig -ErrorAction SilentlyContinue)
                $data.Exchange.CASMailboxPlans = @(Get-CASMailboxPlan -ErrorAction SilentlyContinue)
            } else {
                $data.Errors['Exchange'] = 'EXO module available but not connected. Run Connect-ExchangeOnline first.'
            }
        } catch {
            $data.Errors['Exchange'] = $_.Exception.Message
        }
    } else {
        $data.Errors['Exchange'] = 'ExchangeOnlineManagement module not available — Exchange checks will be skipped'
    }

    # ── SharePoint / OneDrive ─────────────────────────────────────────────
    if (-not $Quiet) {
        Write-ProgressLine -Phase INFILTRATE -Message 'Collecting SharePoint/OneDrive settings via Graph'
    }
    try {
        # SharePoint admin settings via Graph (limited)
        $data.SharePoint.Sites = @(Invoke-GraphApi -AccessToken $AccessToken `
            -Uri '/sites' -QueryParameters @{ 'search' = '*'; '$top' = '10' } `
            -Paginate -MaxPages 1 -Quiet:$Quiet)
    } catch {
        $data.Errors['SharePoint'] = $_.Exception.Message
    }

    # ── Teams ─────────────────────────────────────────────────────────────
    if (-not $Quiet) {
        Write-ProgressLine -Phase INFILTRATE -Message 'Collecting Teams configuration via Graph'
    }
    try {
        $data.Teams.AppCatalogs = @(Invoke-GraphApi -AccessToken $AccessToken `
            -Uri '/appCatalogs/teamsApps' `
            -QueryParameters @{ '$filter' = "distributionMethod eq 'organization'" } `
            -Paginate -Quiet:$Quiet)
    } catch {
        $data.Errors['TeamsApps'] = $_.Exception.Message
    }

    # Teams policies require Teams admin module or beta Graph
    $hasTeams = $ModuleAvailability -and $ModuleAvailability.MicrosoftTeams
    if ($hasTeams) {
        try {
            $teamsConnected = Get-Command Get-CsTeamsMeetingPolicy -ErrorAction SilentlyContinue
            if ($teamsConnected) {
                $data.Teams.MeetingPolicies = @(Get-CsTeamsMeetingPolicy -ErrorAction SilentlyContinue)
                $data.Teams.MessagingPolicies = @(Get-CsTeamsMessagingPolicy -ErrorAction SilentlyContinue)
                $data.Teams.ExternalAccessConfig = Get-CsTenantFederationConfiguration -ErrorAction SilentlyContinue
                $data.Teams.GuestConfig = Get-CsTeamsClientConfiguration -ErrorAction SilentlyContinue
            } else {
                $data.Errors['TeamsAdmin'] = 'Teams module available but not connected. Run Connect-MicrosoftTeams first.'
            }
        } catch {
            $data.Errors['TeamsAdmin'] = $_.Exception.Message
        }
    }

    # ── Defender for Office 365 ───────────────────────────────────────────
    if ($hasEXO) {
        try {
            $exoConnected = Get-Command Get-EOPProtectionPolicyRule -ErrorAction SilentlyContinue
            if ($exoConnected) {
                $data.Defender.ProtectionPolicyRules = @(Get-EOPProtectionPolicyRule -ErrorAction SilentlyContinue)
                $data.Defender.ProtectionAlerts = @(Get-ProtectionAlert -ErrorAction SilentlyContinue)
            }
        } catch {
            $data.Errors['Defender'] = $_.Exception.Message
        }
    }

    # ── Unified Audit Log ─────────────────────────────────────────────────
    if ($hasEXO) {
        try {
            $exoConnected = Get-Command Get-AdminAuditLogConfig -ErrorAction SilentlyContinue
            if ($exoConnected) {
                $data.Audit.AdminAuditLogConfig = Get-AdminAuditLogConfig -ErrorAction SilentlyContinue
            }
        } catch {
            $data.Errors['Audit'] = $_.Exception.Message
        }
    }

    # ── Power Platform ────────────────────────────────────────────────────
    $hasPP = $ModuleAvailability -and $ModuleAvailability.PowerAppsAdmin
    if ($hasPP) {
        if (-not $Quiet) {
            Write-ProgressLine -Phase INFILTRATE -Message 'Collecting Power Platform configuration'
        }
        try {
            $ppConnected = Get-Command Get-AdminPowerAppEnvironment -ErrorAction SilentlyContinue
            if ($ppConnected) {
                $data.PowerPlatform.Environments = @(Get-AdminPowerAppEnvironment -ErrorAction SilentlyContinue)
                $data.PowerPlatform.DlpPolicies = @(Get-DlpPolicy -ErrorAction SilentlyContinue)
                $data.PowerPlatform.TenantSettings = Get-TenantSettings -ErrorAction SilentlyContinue
            }
        } catch {
            $data.Errors['PowerPlatform'] = $_.Exception.Message
        }
    } else {
        $data.Errors['PowerPlatform'] = 'Power Platform admin module not available'
    }

    if (-not $Quiet) {
        $collected = @()
        if ($data.Exchange.Count -gt 1) { $collected += 'Exchange' }
        if ($data.SharePoint.Count -gt 0) { $collected += 'SharePoint' }
        if ($data.Teams.Count -gt 0) { $collected += 'Teams' }
        Write-ProgressLine -Phase INFILTRATE -Message "M365 data collected: $($collected -join ', ')"
    }

    return $data
}