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


$Server = "RUKLGDC01.pcmarus.loc"
$Stamp  = Get-Date -Format "yyyyMMdd_HHmmss"

$RemoteScriptLocal = "C:\Windows\Temp\DHCP_Analysis_$Stamp.ps1"
$RemoteOutLocal    = "C:\Windows\Temp\DHCP_Analysis_$Stamp.txt"
$RemoteDoneLocal   = "C:\Windows\Temp\DHCP_Analysis_$Stamp.done"

$RemoteScriptUNC = "\\$Server\C$\Windows\Temp\DHCP_Analysis_$Stamp.ps1"
$RemoteOutUNC    = "\\$Server\C$\Windows\Temp\DHCP_Analysis_$Stamp.txt"
$RemoteDoneUNC   = "\\$Server\C$\Windows\Temp\DHCP_Analysis_$Stamp.done"

$LocalReportDir = "C:\DHCP_Reports"
$LocalReport    = Join-Path $LocalReportDir "DHCP_Analysis_$Stamp.txt"

$ScriptBody = @'
$ErrorActionPreference = "Continue"
Import-Module DhcpServer -ErrorAction Stop

$TargetScopes = @(
    "10.182.150.0",
    "10.182.151.0",
    "10.182.152.0",
    "10.182.153.0",
    "10.182.154.0",
    "10.182.155.0"
)

& {
    Write-Output "============================================================"
    Write-Output " DHCP DIAGNOSTIC ANALYSIS"
    Write-Output "============================================================"
    Write-Output "Server: $env:COMPUTERNAME"
    Write-Output "Date:   $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
    Write-Output "Mode:   READ ONLY"

    Write-Output "`n=== DHCP SERVICE ==="
    Get-Service DHCPServer |
        Select-Object Name,Status,StartType |
        Format-List

    Write-Output "`n=== TARGET SCOPE CONFIGURATION ==="

    foreach ($ScopeId in $TargetScopes) {
        Get-DhcpServerv4Scope -ScopeId $ScopeId -ErrorAction SilentlyContinue |
            Select-Object ScopeId,Name,State,StartRange,EndRange,
                SubnetMask,LeaseDuration |
            Format-List
    }

    Write-Output "`n=== TARGET SCOPE STATISTICS ==="

    foreach ($ScopeId in $TargetScopes) {
        Get-DhcpServerv4ScopeStatistics `
            -ScopeId $ScopeId `
            -ErrorAction SilentlyContinue |
        Select-Object ScopeId,PercentageInUse,InUse,Free,Reserved,Pending |
        Format-List
    }

    Write-Output "`n=== COLLECTING ACTIVE LEASES ==="

    $AllLeases = foreach ($ScopeId in $TargetScopes) {
        Get-DhcpServerv4Lease `
            -ScopeId $ScopeId `
            -AllLeases `
            -ErrorAction SilentlyContinue |
        Select-Object @{
                Name       = "ScopeId"
                Expression = {$ScopeId}
            },
            IPAddress,
            HostName,
            ClientId,
            AddressState,
            LeaseExpiryTime
    }

    Write-Output "Total leases collected: $($AllLeases.Count)"

    $ActiveLeases = $AllLeases |
        Where-Object {
            $_.AddressState -eq "Active"
        }

    Write-Output "Active leases collected: $($ActiveLeases.Count)"

    Write-Output "`n=== DUPLICATE CLIENT ID INSIDE THE SAME SCOPE ==="

    $DuplicatesSameScope = $ActiveLeases |
        Where-Object {$_.ClientId} |
        Group-Object ScopeId,ClientId |
        Where-Object {$_.Count -gt 1}

    if ($DuplicatesSameScope) {
        foreach ($Group in $DuplicatesSameScope) {
            Write-Output "`nDuplicate group: $($Group.Name)"
            Write-Output "Lease count: $($Group.Count)"

            $Group.Group |
                Sort-Object IPAddress |
                Format-Table ScopeId,IPAddress,HostName,ClientId,
                    AddressState,LeaseExpiryTime -AutoSize
        }
    }
    else {
        Write-Output "No active duplicate ClientId values inside one scope were found."
    }

    Write-Output "`n=== CLIENT ID PRESENT IN MULTIPLE SCOPES ==="

    $DuplicatesAcrossScopes = $ActiveLeases |
        Where-Object {$_.ClientId} |
        Group-Object ClientId |
        Where-Object {
            ($_.Group.ScopeId | Sort-Object -Unique).Count -gt 1
        }

    if ($DuplicatesAcrossScopes) {
        foreach ($Group in $DuplicatesAcrossScopes) {
            Write-Output "`nClientId: $($Group.Name)"
            Write-Output "Scopes: $(
                ($Group.Group.ScopeId | Sort-Object -Unique) -join ', '
            )"

            $Group.Group |
                Sort-Object ScopeId,IPAddress |
                Format-Table ScopeId,IPAddress,HostName,ClientId,
                    AddressState,LeaseExpiryTime -AutoSize
        }
    }
    else {
        Write-Output "No ClientId values occurring in multiple scopes were found."
    }

    Write-Output "`n=== SAME HOSTNAME WITH DIFFERENT CLIENT ID VALUES ==="

    $HostNameDuplicates = $ActiveLeases |
        Where-Object {
            $_.HostName -and
            $_.HostName.Trim() -ne ""
        } |
        Group-Object HostName |
        Where-Object {
            ($_.Group.ClientId | Sort-Object -Unique).Count -gt 1
        }

    if ($HostNameDuplicates) {
        foreach ($Group in $HostNameDuplicates) {
            Write-Output "`nHostName: $($Group.Name)"
            Write-Output "ClientId count: $(
                ($Group.Group.ClientId | Sort-Object -Unique).Count
            )"

            $Group.Group |
                Sort-Object ScopeId,IPAddress |
                Format-Table ScopeId,IPAddress,HostName,ClientId,
                    AddressState,LeaseExpiryTime -AutoSize
        }
    }
    else {
        Write-Output "No hostnames with multiple ClientId values were found."
    }

    Write-Output "`n=== SAME HOSTNAME PRESENT IN MULTIPLE SCOPES ==="

    $HostNamesAcrossScopes = $ActiveLeases |
        Where-Object {
            $_.HostName -and
            $_.HostName.Trim() -ne ""
        } |
        Group-Object HostName |
        Where-Object {
            ($_.Group.ScopeId | Sort-Object -Unique).Count -gt 1
        }

    if ($HostNamesAcrossScopes) {
        foreach ($Group in $HostNamesAcrossScopes) {
            Write-Output "`nHostName: $($Group.Name)"
            Write-Output "Scopes: $(
                ($Group.Group.ScopeId | Sort-Object -Unique) -join ', '
            )"

            $Group.Group |
                Sort-Object ScopeId,IPAddress |
                Format-Table ScopeId,IPAddress,ClientId,
                    AddressState,LeaseExpiryTime -AutoSize
        }
    }
    else {
        Write-Output "No hostnames occurring in multiple scopes were found."
    }

    Write-Output "`n=== LEASES WITHOUT HOSTNAME ==="

    $NoHostName = $ActiveLeases |
        Where-Object {
            -not $_.HostName -or
            $_.HostName.Trim() -eq ""
        }

    $NoHostName |
        Group-Object ScopeId |
        Select-Object Name,Count |
        Format-Table -AutoSize

    Write-Output "`n=== DHCP OPTIONS FOR TARGET SCOPES ==="

    foreach ($ScopeId in $TargetScopes) {
        Write-Output "`nScope: $ScopeId"

        Get-DhcpServerv4OptionValue `
            -ScopeId $ScopeId `
            -ErrorAction SilentlyContinue |
        Format-Table OptionId,Name,Value -AutoSize
    }

    Write-Output "`n=== EXCLUSION RANGES ==="

    foreach ($ScopeId in $TargetScopes) {
        Write-Output "`nScope: $ScopeId"

        $Exclusions = Get-DhcpServerv4ExclusionRange `
            -ScopeId $ScopeId `
            -ErrorAction SilentlyContinue

        if ($Exclusions) {
            $Exclusions |
                Format-Table ScopeId,StartRange,EndRange -AutoSize
        }
        else {
            Write-Output "No exclusions configured."
        }
    }

    Write-Output "`n=== RESERVATIONS ==="

    foreach ($ScopeId in $TargetScopes) {
        Write-Output "`nScope: $ScopeId"

        $Reservations = Get-DhcpServerv4Reservation `
            -ScopeId $ScopeId `
            -ErrorAction SilentlyContinue

        if ($Reservations) {
            $Reservations |
                Format-Table ScopeId,IPAddress,ClientId,Name,Description -AutoSize
        }
        else {
            Write-Output "No reservations configured."
        }
    }

    Write-Output "`n=== DHCP ADMIN EVENTS FOR LAST 7 DAYS ==="

    $StartTime = (Get-Date).AddDays(-7)

    $Events = Get-WinEvent -FilterHashtable @{
        LogName   = "DhcpAdminEvents"
        StartTime = $StartTime
    } -ErrorAction SilentlyContinue |
    Where-Object {
        $_.Level -in 1,2,3
    } |
    Select-Object -First 300 TimeCreated,Id,LevelDisplayName,Message

    if ($Events) {
        Write-Output "`nEvent summary:"

        $Events |
            Group-Object Id |
            Sort-Object Count -Descending |
            Select-Object Name,Count |
            Format-Table -AutoSize

        Write-Output "`nLatest events:"

        $Events |
            Select-Object -First 100 |
            Format-List
    }
    else {
        Write-Output "No errors or warnings were found."
    }

    Write-Output "`n=== EVENT 20287 SUMMARY BY SCOPE ==="

    $PoolErrors = Get-WinEvent -FilterHashtable @{
        LogName   = "DhcpAdminEvents"
        Id        = 20287
        StartTime = $StartTime
    } -ErrorAction SilentlyContinue

    if ($PoolErrors) {
        $ScopeNames = foreach ($Event in $PoolErrors) {
            if ($Event.Message -match "scope/superscope\s+([^\s]+)") {
                $Matches[1]
            }
            else {
                "Unknown"
            }
        }

        $ScopeNames |
            Group-Object |
            Sort-Object Count -Descending |
            Select-Object Name,Count |
            Format-Table -AutoSize
    }
    else {
        Write-Output "Event 20287 was not found during the selected period."
    }

    Write-Output "`n=== CONCLUSION MARKERS ==="
    Write-Output "Duplicate ClientId in same scope: $($DuplicatesSameScope.Count)"
    Write-Output "ClientId in multiple scopes:      $($DuplicatesAcrossScopes.Count)"
    Write-Output "Hostname with different ClientId: $($HostNameDuplicates.Count)"
    Write-Output "Hostname in multiple scopes:      $($HostNamesAcrossScopes.Count)"
    Write-Output "Active leases without hostname:   $($NoHostName.Count)"

    Write-Output "`nDIAGNOSTIC COLLECTION COMPLETED"

} 2>&1 |
    Out-String -Width 400 |
    Set-Content -LiteralPath "__OUT__" -Encoding UTF8

New-Item -ItemType File -Path "__DONE__" -Force | Out-Null
'@

$ScriptBody = $ScriptBody.Replace("__OUT__", $RemoteOutLocal)
$ScriptBody = $ScriptBody.Replace("__DONE__", $RemoteDoneLocal)

try {
    New-Item -ItemType Directory -Path $LocalReportDir -Force |
        Out-Null

    Write-Host "Проверка доступа к DHCP-серверу..." -ForegroundColor Cyan

    if (-not (Test-Path "\\$Server\C$")) {
        throw "Нет доступа к административной шаре \\$Server\C$"
    }

    Write-Host "Копирование диагностического скрипта..." -ForegroundColor Cyan

    Set-Content `
        -LiteralPath $RemoteScriptUNC `
        -Value $ScriptBody `
        -Encoding UTF8 `
        -Force

    $CommandLine = "powershell.exe -NoProfile -NonInteractive " +
        "-ExecutionPolicy Bypass -File `"$RemoteScriptLocal`""

    Write-Host "Запуск диагностики на $Server..." -ForegroundColor Cyan

    $ProcessClass = [wmiclass]"\\$Server\root\cimv2:Win32_Process"
    $Result = $ProcessClass.Create($CommandLine)

    if ($Result.ReturnValue -ne 0) {
        throw "Не удалось запустить процесс. WMI ReturnValue: $($Result.ReturnValue)"
    }

    Write-Host "Процесс запущен. PID: $($Result.ProcessId)" -ForegroundColor Green

    $Deadline = (Get-Date).AddMinutes(5)

    while (
        -not (Test-Path $RemoteDoneUNC) -and
        (Get-Date) -lt $Deadline
    ) {
        Start-Sleep -Seconds 2
    }

    if (-not (Test-Path $RemoteDoneUNC)) {
        throw "Диагностика не завершилась за 5 минут."
    }

    Copy-Item `
        -LiteralPath $RemoteOutUNC `
        -Destination $LocalReport `
        -Force

    Write-Host "`nДиагностика завершена." -ForegroundColor Green
    Write-Host "Отчёт сохранён: $LocalReport" -ForegroundColor Green

    Write-Host "`n=== КРАТКИЙ ВЫВОД ИЗ ОТЧЁТА ===`n" -ForegroundColor Cyan

    Get-Content $LocalReport |
        Select-String -Pattern `
            "Duplicate ClientId in same scope",
            "ClientId in multiple scopes",
            "Hostname with different ClientId",
            "Hostname in multiple scopes",
            "Active leases without hostname",
            "DIAGNOSTIC COLLECTION COMPLETED"
}
catch {
    Write-Host "Ошибка: $($_.Exception.Message)" -ForegroundColor Red

    if (Test-Path $RemoteOutUNC) {
        Copy-Item `
            -LiteralPath $RemoteOutUNC `
            -Destination $LocalReport `
            -Force `
            -ErrorAction SilentlyContinue

        Write-Host "Частичный отчёт сохранён: $LocalReport" -ForegroundColor Yellow
    }
}
finally {
    Remove-Item $RemoteScriptUNC -Force -ErrorAction SilentlyContinue
    Remove-Item $RemoteOutUNC    -Force -ErrorAction SilentlyContinue
    Remove-Item $RemoteDoneUNC   -Force -ErrorAction SilentlyContinue
}