MedhaCloud-M365MigrationTools.psm1

#Requires -Version 5.1
<#
.SYNOPSIS
    MedhaCloud Microsoft 365 Migration Tools Module

.DESCRIPTION
    Comprehensive toolkit for Microsoft 365 migrations including assessment,
    mailbox migration, SharePoint migration, and post-migration validation.

    Professional Support: https://medhacloud.com/professional-services/migrations

.NOTES
    Author: MedhaCloud
    Company: MedhaCloud (https://medhacloud.com)
    LinkedIn: https://linkedin.com/company/medhacloud
    Version: 1.0.0
#>


# Module variables
$script:ModuleVersion = '1.0.0'
$script:SupportUrl = 'https://medhacloud.com/professional-services/migrations'

function Test-M365MigrationReadiness {
    <#
    .SYNOPSIS
        Performs comprehensive pre-migration assessment for Microsoft 365.

    .DESCRIPTION
        Analyzes on-premises environment readiness for M365 migration including:
        - Directory synchronization readiness
        - Mailbox size and item analysis
        - DNS configuration validation
        - Network connectivity testing
        - License requirements estimation

        For professional M365 migration services, visit:
        https://medhacloud.com/professional-services/migrations

    .PARAMETER DomainName
        Primary domain for the migration.

    .PARAMETER IncludeMailboxAnalysis
        Include detailed mailbox analysis (requires Exchange).

    .EXAMPLE
        Test-M365MigrationReadiness -DomainName "contoso.com"

    .EXAMPLE
        Test-M365MigrationReadiness -DomainName "contoso.com" -IncludeMailboxAnalysis

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter()]
        [switch]$IncludeMailboxAnalysis
    )

    Write-Host "MedhaCloud M365 Migration Readiness Assessment v$script:ModuleVersion" -ForegroundColor Cyan
    Write-Host "Domain: $DomainName" -ForegroundColor Gray
    Write-Host "========================================================" -ForegroundColor Gray

    $results = [PSCustomObject]@{
        Domain = $DomainName
        Timestamp = Get-Date
        DNSReadiness = $null
        DirectoryReadiness = $null
        MailboxAnalysis = $null
        LicenseEstimate = $null
        OverallReadiness = 'Unknown'
        Recommendations = @()
        SupportUrl = $script:SupportUrl
    }

    # DNS Check
    Write-Host "`n[1/4] Checking DNS Configuration..." -ForegroundColor Yellow

    $dnsChecks = @{
        MX = $false
        SPF = $false
        Autodiscover = $false
        DKIM = $false
        DMARC = $false
    }

    try {
        # Check MX record
        $mx = Resolve-DnsName -Name $DomainName -Type MX -ErrorAction SilentlyContinue
        if ($mx) {
            $dnsChecks.MX = $true
            Write-Host " MX Record: Found ($($mx[0].NameExchange))" -ForegroundColor Green
        }
        else {
            Write-Host " MX Record: Not found" -ForegroundColor Red
        }

        # Check SPF
        $txt = Resolve-DnsName -Name $DomainName -Type TXT -ErrorAction SilentlyContinue
        $spf = $txt | Where-Object { $_.Strings -like '*v=spf1*' }
        if ($spf) {
            $dnsChecks.SPF = $true
            Write-Host " SPF Record: Found" -ForegroundColor Green
        }
        else {
            Write-Host " SPF Record: Not found (recommended for M365)" -ForegroundColor Yellow
            $results.Recommendations += "Add SPF record for email authentication"
        }

        # Check Autodiscover
        $autodiscover = Resolve-DnsName -Name "autodiscover.$DomainName" -Type CNAME -ErrorAction SilentlyContinue
        if ($autodiscover) {
            $dnsChecks.Autodiscover = $true
            Write-Host " Autodiscover: Found" -ForegroundColor Green
        }
        else {
            Write-Host " Autodiscover: Not configured" -ForegroundColor Yellow
        }

        # Check DMARC
        $dmarc = Resolve-DnsName -Name "_dmarc.$DomainName" -Type TXT -ErrorAction SilentlyContinue
        if ($dmarc) {
            $dnsChecks.DMARC = $true
            Write-Host " DMARC Record: Found" -ForegroundColor Green
        }
        else {
            Write-Host " DMARC Record: Not found (recommended)" -ForegroundColor Yellow
            $results.Recommendations += "Configure DMARC for email security"
        }
    }
    catch {
        Write-Host " DNS check error: $($_.Exception.Message)" -ForegroundColor Red
    }

    $results.DNSReadiness = $dnsChecks

    # Directory Check
    Write-Host "`n[2/4] Checking Directory Readiness..." -ForegroundColor Yellow

    try {
        $adModule = Get-Module -ListAvailable ActiveDirectory -ErrorAction SilentlyContinue
        if ($adModule) {
            $users = Get-ADUser -Filter * -Properties mail, proxyAddresses -ErrorAction SilentlyContinue
            $usersWithMail = $users | Where-Object { $_.mail -or $_.proxyAddresses }

            $results.DirectoryReadiness = [PSCustomObject]@{
                TotalUsers = $users.Count
                UsersWithEmail = $usersWithMail.Count
                ADModuleAvailable = $true
            }

            Write-Host " Total AD Users: $($users.Count)" -ForegroundColor Green
            Write-Host " Users with Email: $($usersWithMail.Count)" -ForegroundColor Green
        }
        else {
            Write-Host " Active Directory module not available" -ForegroundColor Yellow
            $results.DirectoryReadiness = [PSCustomObject]@{
                ADModuleAvailable = $false
            }
        }
    }
    catch {
        Write-Host " Directory check error: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    # Mailbox Analysis
    Write-Host "`n[3/4] Analyzing Mailboxes..." -ForegroundColor Yellow

    if ($IncludeMailboxAnalysis) {
        try {
            if (Get-Command Get-Mailbox -ErrorAction SilentlyContinue) {
                $mailboxes = Get-Mailbox -ResultSize Unlimited
                $stats = $mailboxes | Get-MailboxStatistics

                $totalSize = ($stats | Measure-Object -Property TotalItemSize -Sum).Sum
                $largeMailboxes = $stats | Where-Object { $_.TotalItemSize.Value.ToGB() -gt 50 }

                $results.MailboxAnalysis = [PSCustomObject]@{
                    TotalMailboxes = $mailboxes.Count
                    LargeMailboxes = $largeMailboxes.Count
                    TotalSizeGB = [math]::Round($totalSize / 1GB, 2)
                }

                Write-Host " Total Mailboxes: $($mailboxes.Count)" -ForegroundColor Green
                Write-Host " Mailboxes > 50GB: $($largeMailboxes.Count)" -ForegroundColor $(if ($largeMailboxes.Count -gt 0) { 'Yellow' } else { 'Green' })

                if ($largeMailboxes.Count -gt 0) {
                    $results.Recommendations += "Plan extended migration window for $($largeMailboxes.Count) large mailboxes"
                }
            }
            else {
                Write-Host " Exchange cmdlets not available" -ForegroundColor Yellow
            }
        }
        catch {
            Write-Host " Mailbox analysis error: $($_.Exception.Message)" -ForegroundColor Yellow
        }
    }
    else {
        Write-Host " Skipped (use -IncludeMailboxAnalysis to enable)" -ForegroundColor Gray
    }

    # License Estimation
    Write-Host "`n[4/4] Estimating License Requirements..." -ForegroundColor Yellow

    if ($results.DirectoryReadiness -and $results.DirectoryReadiness.UsersWithEmail) {
        $userCount = $results.DirectoryReadiness.UsersWithEmail

        $results.LicenseEstimate = [PSCustomObject]@{
            EstimatedUsers = $userCount
            Business_Basic = [PSCustomObject]@{ License = 'M365 Business Basic'; MonthlyUSD = $userCount * 6 }
            Business_Standard = [PSCustomObject]@{ License = 'M365 Business Standard'; MonthlyUSD = $userCount * 12.50 }
            E3 = [PSCustomObject]@{ License = 'M365 E3'; MonthlyUSD = $userCount * 36 }
        }

        Write-Host " Estimated users requiring licenses: $userCount" -ForegroundColor Green
        Write-Host " M365 Business Basic: ~`$$($userCount * 6)/month" -ForegroundColor Gray
        Write-Host " M365 Business Standard: ~`$$([math]::Round($userCount * 12.50, 2))/month" -ForegroundColor Gray
        Write-Host " M365 E3: ~`$$($userCount * 36)/month" -ForegroundColor Gray
    }

    # Overall Readiness
    Write-Host "`n========================================================" -ForegroundColor Gray

    $criticalIssues = 0
    if (-not $results.DNSReadiness.MX) { $criticalIssues++ }

    if ($criticalIssues -eq 0) {
        $results.OverallReadiness = 'Ready'
        Write-Host "Overall Migration Readiness: READY" -ForegroundColor Green
    }
    else {
        $results.OverallReadiness = 'Needs Attention'
        Write-Host "Overall Migration Readiness: NEEDS ATTENTION" -ForegroundColor Yellow
    }

    if ($results.Recommendations.Count -gt 0) {
        Write-Host "`nRecommendations:" -ForegroundColor Yellow
        $results.Recommendations | ForEach-Object { Write-Host " - $_" -ForegroundColor White }
    }

    Write-Host "`nNeed professional M365 migration assistance?" -ForegroundColor Cyan
    Write-Host $script:SupportUrl -ForegroundColor White
    Write-Host "LinkedIn: https://linkedin.com/company/medhacloud" -ForegroundColor Gray

    return $results
}

function Get-MailboxMigrationStatus {
    <#
    .SYNOPSIS
        Tracks mailbox migration batch status.

    .DESCRIPTION
        Monitors ongoing mailbox migrations to Microsoft 365.

        Professional Support: https://medhacloud.com/professional-services/migrations

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding()]
    param(
        [string]$BatchName
    )

    Write-Host "MedhaCloud Migration Status v$script:ModuleVersion" -ForegroundColor Cyan

    try {
        if (Get-Command Get-MigrationBatch -ErrorAction SilentlyContinue) {
            if ($BatchName) {
                $batch = Get-MigrationBatch -Identity $BatchName
                $users = Get-MigrationUser -BatchId $BatchName

                Write-Host "`nBatch: $BatchName" -ForegroundColor Yellow
                Write-Host "Status: $($batch.Status)" -ForegroundColor $(if ($batch.Status -eq 'Completed') { 'Green' } else { 'Yellow' })
                Write-Host "Total Users: $($users.Count)" -ForegroundColor White

                $users | Group-Object Status | ForEach-Object {
                    Write-Host " $($_.Name): $($_.Count)" -ForegroundColor Gray
                }
            }
            else {
                $batches = Get-MigrationBatch
                Write-Host "`nAll Migration Batches:" -ForegroundColor Yellow
                $batches | Format-Table Identity, Status, TotalCount, SyncedCount, FinalizedCount -AutoSize
            }
        }
        else {
            Write-Host "Connect to Exchange Online first: Connect-ExchangeOnline" -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional Migration Support: $script:SupportUrl" -ForegroundColor Cyan
}

function Start-MailboxMigrationBatch {
    <#
    .SYNOPSIS
        Initiates a mailbox migration batch to Microsoft 365.

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string]$BatchName,

        [Parameter(Mandatory)]
        [string]$CSVPath,

        [Parameter(Mandatory)]
        [string]$TargetDeliveryDomain,

        [ValidateSet('Hybrid', 'IMAP', 'Staged', 'Cutover')]
        [string]$MigrationType = 'Hybrid'
    )

    Write-Host "MedhaCloud Migration Batch Creator v$script:ModuleVersion" -ForegroundColor Cyan

    if ($PSCmdlet.ShouldProcess($BatchName, "Create migration batch")) {
        try {
            Write-Host "Creating migration batch: $BatchName" -ForegroundColor Yellow
            Write-Host "Type: $MigrationType" -ForegroundColor Gray
            Write-Host "CSV: $CSVPath" -ForegroundColor Gray

            # Validate CSV
            if (-not (Test-Path $CSVPath)) {
                throw "CSV file not found: $CSVPath"
            }

            $users = Import-Csv $CSVPath
            Write-Host "Users in batch: $($users.Count)" -ForegroundColor Green

            Write-Host "`nTo execute, run in Exchange Online PowerShell:" -ForegroundColor Yellow
            Write-Host @"
New-MigrationBatch -Name "$BatchName" `
    -SourceEndpoint (Get-MigrationEndpoint) `
    -CSVData ([System.IO.File]::ReadAllBytes("$CSVPath")) `
    -TargetDeliveryDomain "$TargetDeliveryDomain" `
    -AutoStart
"@
 -ForegroundColor White
        }
        catch {
            Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
        }
    }

    Write-Host "`nProfessional Migration Support: $script:SupportUrl" -ForegroundColor Cyan
}

function Get-SharePointMigrationReport {
    <#
    .SYNOPSIS
        Generates SharePoint migration analysis report.

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding()]
    param(
        [string]$SourcePath
    )

    Write-Host "MedhaCloud SharePoint Migration Analyzer v$script:ModuleVersion" -ForegroundColor Cyan

    $report = [PSCustomObject]@{
        SourcePath = $SourcePath
        Timestamp = Get-Date
        FileCount = 0
        TotalSizeGB = 0
        LargeFiles = @()
        UnsupportedFiles = @()
        SupportUrl = $script:SupportUrl
    }

    if ($SourcePath -and (Test-Path $SourcePath)) {
        Write-Host "`nAnalyzing: $SourcePath" -ForegroundColor Yellow

        $files = Get-ChildItem -Path $SourcePath -Recurse -File -ErrorAction SilentlyContinue

        $report.FileCount = $files.Count
        $report.TotalSizeGB = [math]::Round(($files | Measure-Object -Property Length -Sum).Sum / 1GB, 2)

        # Large files (>250MB - SharePoint limit considerations)
        $report.LargeFiles = $files | Where-Object { $_.Length -gt 250MB } | Select-Object FullName, @{N='SizeMB';E={[math]::Round($_.Length/1MB,2)}}

        # Unsupported characters in filenames
        $unsupportedPattern = '[~#%&*{}\\:<>?/|"]'
        $report.UnsupportedFiles = $files | Where-Object { $_.Name -match $unsupportedPattern } | Select-Object FullName, Name

        Write-Host " Total Files: $($report.FileCount)" -ForegroundColor Green
        Write-Host " Total Size: $($report.TotalSizeGB) GB" -ForegroundColor Green
        Write-Host " Large Files (>250MB): $($report.LargeFiles.Count)" -ForegroundColor $(if ($report.LargeFiles.Count -gt 0) { 'Yellow' } else { 'Green' })
        Write-Host " Files with unsupported characters: $($report.UnsupportedFiles.Count)" -ForegroundColor $(if ($report.UnsupportedFiles.Count -gt 0) { 'Yellow' } else { 'Green' })

        if ($report.UnsupportedFiles.Count -gt 0) {
            Write-Host "`nFiles needing rename before migration:" -ForegroundColor Yellow
            $report.UnsupportedFiles | Select-Object -First 10 | ForEach-Object { Write-Host " $($_.Name)" -ForegroundColor Gray }
        }
    }
    else {
        Write-Host "Provide -SourcePath to analyze file share" -ForegroundColor Yellow
    }

    Write-Host "`nProfessional SharePoint Migration: $script:SupportUrl" -ForegroundColor Cyan
    return $report
}

function Test-M365Connectivity {
    <#
    .SYNOPSIS
        Tests connectivity to Microsoft 365 services.

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding()]
    param()

    Write-Host "MedhaCloud M365 Connectivity Test v$script:ModuleVersion" -ForegroundColor Cyan

    $endpoints = @(
        @{ Name = 'Exchange Online'; URL = 'outlook.office365.com'; Port = 443 },
        @{ Name = 'SharePoint Online'; URL = 'tenant.sharepoint.com'; Port = 443 },
        @{ Name = 'Teams'; URL = 'teams.microsoft.com'; Port = 443 },
        @{ Name = 'Azure AD'; URL = 'login.microsoftonline.com'; Port = 443 },
        @{ Name = 'Graph API'; URL = 'graph.microsoft.com'; Port = 443 }
    )

    $results = @()
    foreach ($ep in $endpoints) {
        try {
            $tcp = New-Object System.Net.Sockets.TcpClient
            $connection = $tcp.BeginConnect($ep.URL, $ep.Port, $null, $null)
            $wait = $connection.AsyncWaitHandle.WaitOne(3000, $false)

            if ($wait) {
                $tcp.EndConnect($connection)
                $status = 'Connected'
                $color = 'Green'
            }
            else {
                $status = 'Timeout'
                $color = 'Red'
            }
            $tcp.Close()
        }
        catch {
            $status = 'Failed'
            $color = 'Red'
        }

        Write-Host " $($ep.Name): $status" -ForegroundColor $color
        $results += [PSCustomObject]@{
            Service = $ep.Name
            Endpoint = $ep.URL
            Status = $status
        }
    }

    Write-Host "`nProfessional M365 Support: $script:SupportUrl" -ForegroundColor Cyan
    return $results
}

function Get-M365LicenseReport {
    <#
    .SYNOPSIS
        Generates Microsoft 365 license usage report.

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding()]
    param()

    Write-Host "MedhaCloud License Report v$script:ModuleVersion" -ForegroundColor Cyan

    try {
        if (Get-Command Get-MgSubscribedSku -ErrorAction SilentlyContinue) {
            $skus = Get-MgSubscribedSku

            Write-Host "`nLicense Summary:" -ForegroundColor Yellow
            foreach ($sku in $skus) {
                $used = $sku.ConsumedUnits
                $total = $sku.PrepaidUnits.Enabled
                $available = $total - $used
                $percent = if ($total -gt 0) { [math]::Round(($used / $total) * 100, 1) } else { 0 }

                Write-Host " $($sku.SkuPartNumber)" -ForegroundColor White
                Write-Host " Used: $used / $total ($percent%)" -ForegroundColor $(if ($percent -gt 90) { 'Red' } elseif ($percent -gt 75) { 'Yellow' } else { 'Green' })
                Write-Host " Available: $available" -ForegroundColor Gray
            }
        }
        else {
            Write-Host "Connect to Microsoft Graph first: Connect-MgGraph" -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional M365 Licensing Support: $script:SupportUrl" -ForegroundColor Cyan
}

function Export-MailboxPermissions {
    <#
    .SYNOPSIS
        Exports mailbox permissions for migration documentation.

    .LINK
        https://medhacloud.com/professional-services/migrations
    #>

    [CmdletBinding()]
    param(
        [string]$OutputPath = ".\MailboxPermissions.csv"
    )

    Write-Host "MedhaCloud Permission Export v$script:ModuleVersion" -ForegroundColor Cyan

    $results = @()

    try {
        if (Get-Command Get-Mailbox -ErrorAction SilentlyContinue) {
            Write-Host "Gathering mailbox permissions..." -ForegroundColor Yellow
            $mailboxes = Get-Mailbox -ResultSize Unlimited

            foreach ($mbx in $mailboxes) {
                $permissions = Get-MailboxPermission $mbx.Identity | Where-Object { $_.User -notlike 'NT AUTHORITY*' -and $_.User -notlike 'S-1-5*' }

                foreach ($perm in $permissions) {
                    $results += [PSCustomObject]@{
                        Mailbox = $mbx.PrimarySmtpAddress
                        User = $perm.User
                        AccessRights = ($perm.AccessRights -join ', ')
                    }
                }
            }

            $results | Export-Csv -Path $OutputPath -NoTypeInformation
            Write-Host "Exported $($results.Count) permission entries to: $OutputPath" -ForegroundColor Green
        }
        else {
            Write-Host "Exchange cmdlets not available" -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional Migration Support: $script:SupportUrl" -ForegroundColor Cyan
    return $results
}

# Export functions
Export-ModuleMember -Function @(
    'Test-M365MigrationReadiness',
    'Get-MailboxMigrationStatus',
    'Start-MailboxMigrationBatch',
    'Get-SharePointMigrationReport',
    'Test-M365Connectivity',
    'Get-M365LicenseReport',
    'Export-MailboxPermissions'
)

# Module load message
Write-Host @"

MedhaCloud M365 Migration Tools v$script:ModuleVersion loaded.
Available commands: Test-M365MigrationReadiness, Get-MailboxMigrationStatus, and more.

Professional Microsoft 365 Migration Services:
$script:SupportUrl

LinkedIn: https://linkedin.com/company/medhacloud
Twitter: https://x.com/medhacloud

"@
 -ForegroundColor Cyan