Загрузка данных


# =====================================================================
# Diagnostic collector for Andrey Terekhin
# Exchange Server 2019
#
# READ ONLY:
# - ничего не меняет
# - не запускает Managed Folder Assistant
# - не назначает Retention Policy
# - Search-Mailbox используется только с -EstimateResultOnly
# =====================================================================

$ErrorActionPreference = "Continue"

# ---------------------------------------------------------------------
# SETTINGS
# ---------------------------------------------------------------------

$Identity = "Andrey.Terekhin@fmc-ag.ru"

# Та же дата, которая использовалась исходным collector:
$CutoffDate = [datetime]"2025-07-23"

$TimeStamp = Get-Date -Format "yyyyMMdd_HHmmss"

$OutDir = Join-Path $PWD "Terekhin_Diagnostics_$TimeStamp"

New-Item -Path $OutDir -ItemType Directory -Force | Out-Null

$MainLog = Join-Path $OutDir "00_Summary.txt"

function Write-Log {
    param(
        [string]$Text
    )

    $Text | Tee-Object -FilePath $MainLog -Append
}

Write-Log "============================================================"
Write-Log "Exchange Archive Diagnostic Collector"
Write-Log "Mailbox: $Identity"
Write-Log "Collected: $(Get-Date)"
Write-Log "CutoffDate: $($CutoffDate.ToString('yyyy-MM-dd'))"
Write-Log "Server: $env:COMPUTERNAME"
Write-Log "============================================================"
Write-Log ""


# =====================================================================
# 1. MAILBOX CONFIGURATION
# =====================================================================

Write-Host "[1/9] Mailbox configuration..." -ForegroundColor Yellow

try {

    $Mailbox = Get-Mailbox -Identity $Identity -ErrorAction Stop

    $Mailbox |
        Format-List `
            DisplayName,
            Identity,
            DistinguishedName,
            PrimarySmtpAddress,
            Alias,
            Database,
            ExchangeGuid,
            ArchiveStatus,
            ArchiveState,
            ArchiveDatabase,
            ArchiveGuid,
            ArchiveName,
            RetentionPolicy,
            RetentionHoldEnabled,
            RetentionComment,
            RetentionUrl,
            LitigationHoldEnabled,
            InPlaceHolds,
            ElcProcessingDisabled,
            SingleItemRecoveryEnabled,
            RecoverableItemsQuota,
            RecoverableItemsWarningQuota |
        Out-File `
            -FilePath (Join-Path $OutDir "01_Mailbox_Config.txt") `
            -Encoding UTF8

    Write-Log "Mailbox found."
    Write-Log "Database        : $($Mailbox.Database)"
    Write-Log "ArchiveStatus   : $($Mailbox.ArchiveStatus)"
    Write-Log "ArchiveDatabase : $($Mailbox.ArchiveDatabase)"
    Write-Log "RetentionPolicy : $($Mailbox.RetentionPolicy)"
}
catch {

    Write-Log "ERROR Get-Mailbox: $($_.Exception.Message)"
}


# =====================================================================
# 2. PRIMARY MAILBOX STATISTICS
# =====================================================================

Write-Host "[2/9] Primary mailbox statistics..." -ForegroundColor Yellow

try {

    $PrimaryStats = Get-MailboxStatistics `
        -Identity $Identity `
        -ErrorAction Stop

    $PrimaryStats |
        Format-List `
            DisplayName,
            Database,
            MailboxGuid,
            ItemCount,
            TotalItemSize,
            TotalDeletedItemSize,
            DeletedItemCount,
            LastLogonTime,
            LastLoggedOnUserAccount,
            DisconnectReason,
            IsArchiveMailbox |
        Out-File `
            -FilePath (Join-Path $OutDir "02_Primary_Statistics.txt") `
            -Encoding UTF8

    Write-Log ""
    Write-Log "PRIMARY:"
    Write-Log "ItemCount     : $($PrimaryStats.ItemCount)"
    Write-Log "TotalItemSize : $($PrimaryStats.TotalItemSize)"
}
catch {

    Write-Log "ERROR Primary statistics: $($_.Exception.Message)"
}


# =====================================================================
# 3. ARCHIVE MAILBOX STATISTICS
# =====================================================================

Write-Host "[3/9] Archive mailbox statistics..." -ForegroundColor Yellow

try {

    $ArchiveStats = Get-MailboxStatistics `
        -Identity $Identity `
        -Archive `
        -ErrorAction Stop

    $ArchiveStats |
        Format-List `
            DisplayName,
            Database,
            MailboxGuid,
            ItemCount,
            TotalItemSize,
            TotalDeletedItemSize,
            DeletedItemCount,
            LastLogonTime,
            DisconnectReason,
            IsArchiveMailbox |
        Out-File `
            -FilePath (Join-Path $OutDir "03_Archive_Statistics.txt") `
            -Encoding UTF8

    Write-Log ""
    Write-Log "ARCHIVE:"
    Write-Log "ItemCount     : $($ArchiveStats.ItemCount)"
    Write-Log "TotalItemSize : $($ArchiveStats.TotalItemSize)"
}
catch {

    Write-Log "ERROR Archive statistics: $($_.Exception.Message)"
}


# =====================================================================
# 4. PRIMARY MAILBOX FOLDER STATISTICS
# =====================================================================

Write-Host "[4/9] Primary mailbox folder statistics..." -ForegroundColor Yellow

try {

    Get-MailboxFolderStatistics `
        -Identity $Identity `
        -ErrorAction Stop |
        Select-Object `
            Name,
            FolderPath,
            FolderType,
            ItemsInFolder,
            ItemsInFolderAndSubfolders,
            FolderSize,
            FolderAndSubfolderSize,
            OldestItemReceivedDate,
            NewestItemReceivedDate |
        Export-Csv `
            -Path (Join-Path $OutDir "04_Primary_Folders.csv") `
            -NoTypeInformation `
            -Encoding UTF8
}
catch {

    Write-Log "ERROR Primary folder statistics: $($_.Exception.Message)"
}


# =====================================================================
# 5. ARCHIVE MAILBOX FOLDER STATISTICS
# =====================================================================

Write-Host "[5/9] Archive mailbox folder statistics..." -ForegroundColor Yellow

try {

    Get-MailboxFolderStatistics `
        -Identity $Identity `
        -Archive `
        -ErrorAction Stop |
        Select-Object `
            Name,
            FolderPath,
            FolderType,
            ItemsInFolder,
            ItemsInFolderAndSubfolders,
            FolderSize,
            FolderAndSubfolderSize,
            OldestItemReceivedDate,
            NewestItemReceivedDate |
        Export-Csv `
            -Path (Join-Path $OutDir "05_Archive_Folders.csv") `
            -NoTypeInformation `
            -Encoding UTF8
}
catch {

    Write-Log "ERROR Archive folder statistics: $($_.Exception.Message)"
}


# =====================================================================
# 6. RETENTION POLICY + TAGS
# =====================================================================

Write-Host "[6/9] Retention policy..." -ForegroundColor Yellow

try {

    $Mailbox = Get-Mailbox -Identity $Identity -ErrorAction Stop

    if ($Mailbox.RetentionPolicy) {

        $Policy = Get-RetentionPolicy `
            -Identity $Mailbox.RetentionPolicy `
            -ErrorAction Stop

        $Policy |
            Format-List * |
            Out-File `
                -FilePath (Join-Path $OutDir "06_RetentionPolicy.txt") `
                -Encoding UTF8

        $Tags = foreach ($TagName in $Policy.RetentionPolicyTagLinks) {

            try {

                Get-RetentionPolicyTag `
                    -Identity $TagName `
                    -ErrorAction Stop |
                    Select-Object `
                        Name,
                        Type,
                        RetentionEnabled,
                        AgeLimitForRetention,
                        RetentionAction,
                        MessageClass,
                        TriggerForRetention,
                        SystemTag
            }
            catch {

                [PSCustomObject]@{
                    Name                 = $TagName
                    Type                 = $null
                    RetentionEnabled     = $null
                    AgeLimitForRetention = $null
                    RetentionAction      = $null
                    MessageClass         = $null
                    TriggerForRetention  = $null
                    SystemTag            = $null
                }
            }
        }

        $Tags |
            Export-Csv `
                -Path (Join-Path $OutDir "07_RetentionPolicyTags.csv") `
                -NoTypeInformation `
                -Encoding UTF8
    }
    else {

        Write-Log "WARNING: RetentionPolicy is empty."
    }
}
catch {

    Write-Log "ERROR Retention Policy: $($_.Exception.Message)"
}


# =====================================================================
# 7. MANAGED FOLDER ASSISTANT / ELC DIAGNOSTICS
# =====================================================================

Write-Host "[7/9] Managed Folder Assistant diagnostics..." -ForegroundColor Yellow

try {

    $Diagnostic = Export-MailboxDiagnosticLogs `
        -Identity $Identity `
        -ExtendedProperties `
        -ErrorAction Stop

    $Diagnostic.MailboxLog |
        Out-File `
            -FilePath (Join-Path $OutDir "08_MailboxDiagnosticLogs_RAW.xml") `
            -Encoding UTF8

    try {

        [xml]$Xml = $Diagnostic.MailboxLog

        $ELCProperties = @(
            $Xml.Properties.MailboxTable.Property |
            Where-Object {
                $_.Name -like "ELC*" -or
                $_.Name -like "*Retention*" -or
                $_.Name -like "*Archive*"
            } |
            Select-Object Name,Value
        )

        $ELCProperties |
            Export-Csv `
                -Path (Join-Path $OutDir "09_ELC_Properties.csv") `
                -NoTypeInformation `
                -Encoding UTF8

        Write-Log ""
        Write-Log "ELC/MFA properties:"

        foreach ($Property in $ELCProperties) {
            Write-Log "$($Property.Name) = $($Property.Value)"
        }
    }
    catch {

        Write-Log "ERROR parsing MailboxDiagnosticLogs XML: $($_.Exception.Message)"
    }
}
catch {

    Write-Log "ERROR Export-MailboxDiagnosticLogs: $($_.Exception.Message)"
}


# =====================================================================
# 8. SEARCH-MAILBOX ESTIMATES
#
# ВАЖНО:
# запросы выполняются ПОСЛЕ включения архива.
#
# -DoNotIncludeArchive означает, что мы сейчас проверяем,
# сколько подходящих писем осталось В PRIMARY.
#
# Ничего не копируется и не удаляется.
# =====================================================================

Write-Host "[8/9] Search-Mailbox estimates..." -ForegroundColor Yellow

$SearchResults = New-Object System.Collections.Generic.List[object]

function Invoke-DiagnosticSearch {

    param(
        [string]$Name,
        [string]$Query
    )

    Write-Host "      $Name" -ForegroundColor DarkGray

    try {

        $Result = Search-Mailbox `
            -Identity $Identity `
            -SearchQuery $Query `
            -EstimateResultOnly `
            -DoNotIncludeArchive `
            -SearchDumpster:$false `
            -WarningAction SilentlyContinue `
            -ErrorAction Stop |
            Select-Object -First 1

        $SearchResults.Add(
            [PSCustomObject]@{
                Name             = $Name
                SearchQuery      = $Query
                ResultItemsCount = $Result.ResultItemsCount
                ResultItemsSize  = [string]$Result.ResultItemsSize
                Error            = $null
            }
        )
    }
    catch {

        $SearchResults.Add(
            [PSCustomObject]@{
                Name             = $Name
                SearchQuery      = $Query
                ResultItemsCount = $null
                ResultItemsSize  = $null
                Error            = $_.Exception.Message
            }
        )
    }
}


# ---------------------------------------------------------------------
# Исходный запрос из collector.
# ---------------------------------------------------------------------

Invoke-DiagnosticSearch `
    -Name "Original_BroadQuery" `
    -Query "Kind:email AND Received<07/23/2025"


# ---------------------------------------------------------------------
# Несколько независимых диапазонов.
#
# Нам они нужны для диагностики того, как Search-Mailbox
# интерпретирует даты.
#
# Эти результаты НЕ суммировать как новый migration estimate.
# ---------------------------------------------------------------------

Invoke-DiagnosticSearch `
    -Name "Before_2020" `
    -Query 'Kind:email AND Received<01/01/2020'

Invoke-DiagnosticSearch `
    -Name "2020" `
    -Query 'Kind:email AND Received>=01/01/2020 AND Received<01/01/2021'

Invoke-DiagnosticSearch `
    -Name "2021" `
    -Query 'Kind:email AND Received>=01/01/2021 AND Received<01/01/2022'

Invoke-DiagnosticSearch `
    -Name "2022" `
    -Query 'Kind:email AND Received>=01/01/2022 AND Received<01/01/2023'

Invoke-DiagnosticSearch `
    -Name "2023" `
    -Query 'Kind:email AND Received>=01/01/2023 AND Received<01/01/2024'

Invoke-DiagnosticSearch `
    -Name "2024" `
    -Query 'Kind:email AND Received>=01/01/2024 AND Received<01/01/2025'

Invoke-DiagnosticSearch `
    -Name "2025_to_cutoff" `
    -Query 'Kind:email AND Received>=01/01/2025 AND Received<07/23/2025'


$SearchResults |
    Export-Csv `
        -Path (Join-Path $OutDir "10_SearchMailbox_CurrentPrimary.csv") `
        -NoTypeInformation `
        -Encoding UTF8


Write-Log ""
Write-Log "CURRENT SEARCH-MAILBOX RESULTS:"

foreach ($Result in $SearchResults) {

    Write-Log ""
    Write-Log "[$($Result.Name)]"
    Write-Log "Query : $($Result.SearchQuery)"
    Write-Log "Count : $($Result.ResultItemsCount)"
    Write-Log "Size  : $($Result.ResultItemsSize)"

    if ($Result.Error) {
        Write-Log "ERROR : $($Result.Error)"
    }
}


# =====================================================================
# 9. DATABASE COPY / ARCHIVE DATABASE INFO
# =====================================================================

Write-Host "[9/9] Database information..." -ForegroundColor Yellow

try {

    $Mailbox = Get-Mailbox -Identity $Identity

    $DatabaseNames = @(
        [string]$Mailbox.Database
        [string]$Mailbox.ArchiveDatabase
    ) |
    Where-Object {
        -not [string]::IsNullOrWhiteSpace($_)
    } |
    Select-Object -Unique

    foreach ($DatabaseName in $DatabaseNames) {

        $SafeName = $DatabaseName -replace '[\\/:*?"<>|]', '_'

        try {

            Get-MailboxDatabase `
                -Identity $DatabaseName `
                -Status |
                Format-List `
                    Name,
                    Server,
                    Mounted,
                    DatabaseSize,
                    AvailableNewMailboxSpace,
                    EdbFilePath,
                    LogFolderPath |
                Out-File `
                    -FilePath (
                        Join-Path $OutDir "11_Database_$SafeName.txt"
                    ) `
                    -Encoding UTF8
        }
        catch {

            Write-Log "ERROR database $DatabaseName : $($_.Exception.Message)"
        }
    }
}
catch {

    Write-Log "ERROR database collection: $($_.Exception.Message)"
}


# =====================================================================
# FINISH
# =====================================================================

Write-Log ""
Write-Log "============================================================"
Write-Log "Collection completed: $(Get-Date)"
Write-Log "Output directory: $OutDir"
Write-Log "============================================================"

$ZipPath = "$OutDir.zip"

try {

    Compress-Archive `
        -Path (Join-Path $OutDir "*") `
        -DestinationPath $ZipPath `
        -Force

    Write-Host ""
    Write-Host "DONE" -ForegroundColor Green
    Write-Host "ZIP: $ZipPath" -ForegroundColor Green
}
catch {

    Write-Host ""
    Write-Host "Collection completed, but ZIP creation failed:" -ForegroundColor Yellow
    Write-Host $_.Exception.Message -ForegroundColor Yellow
    Write-Host "Folder: $OutDir" -ForegroundColor Green
}