Public/Invoke-MigrationAssessment.ps1

function Invoke-MigrationAssessment {
    <#
    .SYNOPSIS
        Runs a complete Exchange-to-M365 migration pre-assessment.
 
    .DESCRIPTION
        Gathers all data needed to plan an Exchange migration:
        1. Mailbox inventory with sizes and types
        2. Distribution group audit
        3. Mail flow (transport) rule export
        4. Public folder analysis
 
        Generates an HTML report with migration readiness summary,
        estimated migration time, and potential blockers.
 
    .PARAMETER OutputPath
        Directory for the HTML report and CSV exports. Defaults to .\Reports.
 
    .PARAMETER ExportCsv
        Also export detailed data as CSV files alongside the HTML report.
 
    .PARAMETER SkipPublicFolders
        Skip the public folder analysis (useful if no public folders exist).
 
    .EXAMPLE
        Invoke-MigrationAssessment
 
    .EXAMPLE
        Invoke-MigrationAssessment -ExportCsv -OutputPath "\\server\migration-planning"
 
    .NOTES
        Run from the Exchange Management Shell or with Exchange Online Management module.
    #>

    [CmdletBinding()]
    param(
        [string]$OutputPath = '.\Reports',

        [switch]$ExportCsv,

        [switch]$SkipPublicFolders,

        [string]$LogPath = '.\Logs'
    )

    begin {
        foreach ($dir in @($OutputPath, $LogPath)) {
            if (-not (Test-Path $dir)) {
                New-Item -Path $dir -ItemType Directory -Force | Out-Null
            }
        }

        $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
        Start-Transcript -Path (Join-Path $LogPath "MigrationAssessment-$timestamp.log") -Append

        $assessment = @{}
    }

    process {
        # 1. Mailbox Inventory
        Write-Verbose "Gathering mailbox inventory..."
        $assessment['Mailboxes'] = @(Get-MailboxInventory)

        # 2. Distribution Groups
        Write-Verbose "Auditing distribution groups..."
        $assessment['DistGroups'] = @(Get-DistributionGroupAudit)

        # 3. Mail Flow Rules
        Write-Verbose "Exporting mail flow rules..."
        $assessment['MailFlowRules'] = @(Get-MailFlowRuleExport)

        # 4. Public Folders
        if (-not $SkipPublicFolders) {
            Write-Verbose "Analyzing public folders..."
            $assessment['PublicFolders'] = @(Get-PublicFolderAnalysis)
        }

        # Export CSVs if requested
        if ($ExportCsv) {
            $assessment['Mailboxes']    | Export-Csv (Join-Path $OutputPath "Mailboxes-$timestamp.csv") -NoTypeInformation
            $assessment['DistGroups']   | Export-Csv (Join-Path $OutputPath "DistGroups-$timestamp.csv") -NoTypeInformation
            $assessment['MailFlowRules'] | Export-Csv (Join-Path $OutputPath "MailFlowRules-$timestamp.csv") -NoTypeInformation
            if ($assessment['PublicFolders']) {
                $assessment['PublicFolders'] | Export-Csv (Join-Path $OutputPath "PublicFolders-$timestamp.csv") -NoTypeInformation
            }
            Write-Verbose "CSV files exported to $OutputPath"
        }

        # Generate HTML
        $htmlFile = Join-Path $OutputPath "MigrationAssessment-$timestamp.html"
        $html = _New-MigrationAssessmentHtml -Assessment $assessment
        $html | Out-File -FilePath $htmlFile -Encoding UTF8
        Write-Verbose "Report saved: $htmlFile"

        # Summary calculations
        $totalMailboxes = $assessment['Mailboxes'].Count
        $totalSizeGB = ($assessment['Mailboxes'] | Measure-Object -Property SizeGB -Sum).Sum
        $largeMailboxes = @($assessment['Mailboxes'] | Where-Object { $_.SizeGB -gt 50 }).Count

        [PSCustomObject]@{
            AssessmentDate    = Get-Date -Format 'yyyy-MM-dd HH:mm'
            TotalMailboxes    = $totalMailboxes
            TotalSizeGB       = [math]::Round($totalSizeGB, 1)
            LargeMailboxes    = $largeMailboxes
            DistributionGroups = $assessment['DistGroups'].Count
            MailFlowRules     = $assessment['MailFlowRules'].Count
            PublicFolders     = if ($assessment['PublicFolders']) { $assessment['PublicFolders'].Count } else { 'Skipped' }
            ReportPath        = $htmlFile
        }
    }

    end {
        Stop-Transcript
    }
}