Blog

PowerShell ile Hyper-V Raporlama ve Yönetim

Dijital dönüşüm ve sanallaştırmanın hız kazandığı günümüzde, kurumların büyük çoğunluğu altyapılarında Hyper-V Cluster mimarisini tercih ediyor. Çünkü hem esneklik, hem kaynak optimizasyonu, hem de iş sürekliliği için güçlü bir temel sağlıyor. Ancak büyüyen sanal ortamların yönetiminde en büyük zorluklardan biri, anlık görünürlük, kaynak sağlığı ve risklerin öngörülmesi oluyor.

  • Birden fazla cluster, onlarca host, yüzlerce VM
  • Her gün değişen snapshot’lar, artan disk dosyaları, hızla tükenen kaynaklar
  • Sürpriz servis kesintileri, plansız kapasite sorunları ve unutulan checkpoint’ler

Bütün bu riskler; “Benim ortamımda neler oluyor?” sorusuna hızlı, kapsamlı ve okunabilir yanıtlar almayı zorunlu kılıyor.
İşte tam da bu noktada, uçtan uca bir otomasyonla Hyper-V altyapısının nabzını tutmak hem operasyonel riskleri azaltıyor hem de zamandan tasarruf sağlıyor.

Neden Otomatik Hyper-V Cluster Raporlama Script’i?

Manuel kontroller büyük yapılarda mümkün değil. PowerShell ile yazılmış bu raporlama script’i kurumlara şu avantajları getiriyor:

  • Bütünsel Bakış Açısı: Tüm cluster’larınızı ve host’larınızı tek bir raporla yönetici paneline dönüştürür.
  • Donanım ve Kaynak Yönetimi: Her host’un CPU, bellek ve işletim sistemi detayını; VM’lerin donanım, memory, checkpoint ve disk bilgilerini tek bakışta sunar.
  • Proaktif Uyarı Mekanizması: Kapasite taşmaları, aşırı snapshot birikimleri, riskli VHD zincirleri gibi sorunları oluşmadan görme imkanı sağlar.
  • Kurum Standartlarında Raporlama: Modern, okunabilir HTML raporu her gün otomatik olarak IT ekibinin posta kutusuna gelir.
  • Tam Otomasyon: Elle hiçbir müdahale olmadan, Windows Görev Zamanlayıcı ile her sabah güncel rapor elinizde olur.

Script’in Mimarisine Detaylı Bir Bakış

Çoklu Cluster Desteği ve Modüler Yapı

Script’in başında raporlanacak tüm Hyper-V cluster adlarını tanımlayabilirsiniz. Böylece farklı lokasyonlardaki, farklı roller üstlenen tüm Hyper-V ortamlarını merkezi olarak takip edebilirsiniz.

Donanım ve VM Envanteri

Get-HostHardwareInfo fonksiyonu ile her host’un:

  • Fiziksel işlemci soket sayısı
  • Toplam core ve logical CPU sayısı
  • Hyperthreading durumu (güvenlik ve performans için kritik)
  • Bellek durumu (toplam/kullanılan/boş RAM)
  • Üzerindeki VM sayısı
  • İşletim sistemi versiyonu otomatik şekilde toplanır.

Tüm Cluster ve Host’lardan Toplama

Script, belirttiğiniz tüm cluster’larda hostları otomatik olarak döngüye alır, her bir host ve üzerindeki VM’lerden bilgi toplar. Burada özellikle aşağıdaki detaylar öne çıkıyor:

  • Sıfır manuel müdahele: Hiçbir hostname elle girilmiyor; tüm işlemler dinamik.
  • Paralel sağlık kontrolü: Her host ve VM için eş zamanlı raporlama.
  • Kapsamlı özet tablolar: Cluster bazında toplam VM sayısı ayrı tabloda, host ve VM detayları ayrı tabloda gösteriliyor.

HTML Formatında Zengin Raporlama

Toplanan tüm bilgiler, profesyonel HTML tablo formatında sunuluyor. HTML raporda:

  • Kurumsal renkler, okunabilir tasarım
  • Ana başlıkta tarih ve saat bilgisi
  • Cluster bazında VM sayısı özet tablo
  • Host donanım detayları
  • VM detayları (gelişmiş checkpoint ve disk bilgileriyle birlikte)
  • Tablolar arası bağlantı ve düzen

Otomatik E-Posta Entegrasyonu

Hazırlanan HTML rapor, belirlenen SMTP relay (ör: kurumunuzun relay.saglik.gov.tr SMTP sunucusu) ile doğrudan hedef mail adreslerine gönderiliyor.
Gönderim parametreleri tamamen dinamik ve güvenli.

Snapshot/Checkpoint Temizliği

Unutulan ve birikmiş snapshot’lar sanal altyapının performansını ciddi şekilde olumsuz etkiler. Rapor ile hangi VM’lerde ne kadar snapshot olduğunu ve adlarını otomatik görebilirsiniz.

Disk Zinciri ve Depolama Yönetimi

VM’lere ait VHD yolları, zincirli disk yapısı varsa kolayca tespit edilir. Depolama alarmı vermeden önce VM seviyesinde önlem alabilirsiniz.

İlgili Script aşağıda yer almaktadır.

# Cluster ve Mail Ayarları
$Clusters = @("Cluster İsimleri Yazılır")

$MailFrom = "[email protected]"
$MailTo = "[email protected]"
$SMTPServer = "smptp.domain.com.tr"
$MailSubject = "Hyper-V Cluster Detayli Rapor - $(Get-Date -Format 'yyyy-MM-dd')"

# --- VM Restart Pending Kontrol Fonksiyonu ---
function Test-VMRestartPending {
    param(
        [string]$VMName,
        [string]$VMHost
    )

    $session = $null
    try {
        $session = New-PSSession -ComputerName $VMName -ErrorAction Stop

        $scriptBlock = {
            $restartPending = $false
            try {
                $cbReboot = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Name RebootPending -ErrorAction SilentlyContinue
                $updateVolatile = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" -ErrorAction SilentlyContinue
                $pendingRename = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -ErrorAction SilentlyContinue

                if ($cbReboot -or $updateVolatile -or $pendingRename) {
                    $restartPending = $true
                }
            } catch {
                $restartPending = $false
            }
            return $restartPending
        }

        $result = Invoke-Command -Session $session -ScriptBlock $scriptBlock
        Remove-PSSession -Session $session
        return $result
    }
    catch {
        if ($session) { Remove-PSSession -Session $session }
        return $false
    }
}

# --- Host Donanım Bilgisi Fonksiyonu ---
function Get-HostHardwareInfo {
    param($HostName, $VMCount, $ClusterName)

    $proc = Get-WmiObject Win32_Processor -ComputerName $HostName -ErrorAction SilentlyContinue
    $mem = Get-WmiObject Win32_ComputerSystem -ComputerName $HostName -ErrorAction SilentlyContinue
    $os = Get-WmiObject Win32_OperatingSystem -ComputerName $HostName -ErrorAction SilentlyContinue
    $disks = Get-WmiObject Win32_LogicalDisk -ComputerName $HostName -Filter "DriveType=3 AND DeviceID != 'C:'" -ErrorAction SilentlyContinue

    $socketCount = ($proc | Select-Object -ExpandProperty SocketDesignation | Sort-Object -Unique).Count
    $coreCount = ($proc | Measure-Object -Property NumberOfCores -Sum).Sum
    $logicalCount = ($proc | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
    $hyperthreading = if ($coreCount -and $logicalCount -and $logicalCount -gt $coreCount) { "Enabled" } else { "Disabled" }
    $totalMem = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
    $freeMem = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
    $usedMem = $totalMem - $freeMem

    $diskDetails = $disks | ForEach-Object {
        $freePercent = if ($_.Size -ne 0) { [math]::Round(($_.FreeSpace / $_.Size) * 100, 1) } else { 0 }
        "$($_.VolumeName) ($($_.DeviceID)) - $freePercent% Free"
    } | Sort-Object
    $diskDetailsStr = $diskDetails -join "<br>"

    $restartedRecently = $false
    try {
        $uptimeEvent = Get-WmiObject Win32_OperatingSystem -ComputerName $HostName -ErrorAction Stop
        if ($uptimeEvent -and $uptimeEvent.LastBootUpTime) {
            $lastBoot = $uptimeEvent.ConvertToDateTime($uptimeEvent.LastBootUpTime)
            if ((Get-Date) - $lastBoot -lt (New-TimeSpan -Days 7)) {
                $restartedRecently = $true
            }
        }
    } catch {}

    $restartPending = $false
    try {
        $cbReboot = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Name RebootPending -ErrorAction SilentlyContinue
        $updateVolatile = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" -ErrorAction SilentlyContinue
        $pendingRename = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -ErrorAction SilentlyContinue

        if ($cbReboot -or $updateVolatile -or $pendingRename) {
            $restartPending = $true
        }
    } catch {}

    [PSCustomObject]@{
        ClusterName      = $ClusterName
        HostName         = $HostName
        PhysicalCPU      = $socketCount
        CoreCount        = $coreCount
        LogicalCPU       = $logicalCount
        Hyperthreading   = $hyperthreading
        MemTotalGB       = $totalMem
        MemUsedGB        = $usedMem
        MemFreeGB        = $freeMem
        VMCount          = $VMCount
        OSVersion        = $os.Caption
        DiskDetails      = $diskDetailsStr
        RestartedRecent  = $restartedRecently
        RestartPending   = $restartPending
    }
}

# --- VM Bilgisi Fonksiyonu ---
function Get-VMFullInfo {
    param($VM, $NodeName)
    $cpucount = $VM.ProcessorCount
    $memAssigned = [math]::Round($VM.MemoryAssigned / 1MB, 2)
    $memStartup = [math]::Round($VM.MemoryStartup / 1MB, 2)
    $memMin = [math]::Round($VM.MemoryMinimum / 1MB, 2)
    $memMax = [math]::Round($VM.MemoryMaximum / 1MB, 2)
    $chkpoints = (Get-VMSnapshot -VMName $VM.Name -ComputerName $NodeName -ErrorAction SilentlyContinue)
    $chkpointCount = if ($chkpoints) { $chkpoints.Count } else { 0 }
    $chkpointNames = if ($chkpoints) { $chkpoints | Select-Object -ExpandProperty Name -Unique } else { @() }
    $vhdDisks = $VM.HardDrives | Select-Object -ExpandProperty Path

    $restartPendingVM = Test-VMRestartPending -VMName $VM.Name -VMHost $NodeName

    [PSCustomObject]@{
        VMName         = $VM.Name
        Node           = $NodeName
        vCPU           = $cpucount
        MemStartupMB   = $memStartup
        MemMinMB       = $memMin
        MemMaxMB       = $memMax
        MemAssignedMB  = $memAssigned
        CheckpointCount= $chkpointCount
        CheckpointNames= ($chkpointNames -join ", ")
        VHDPaths       = ($vhdDisks -join "; ")
        RestartPending = $restartPendingVM
    }
}

# --- Cluster Toplama ---
$hostHwList = @()
$vmFullInfo = @()
$clusterVMCount = @{}
$clusterHostCount = @{}
$clusterRestartedHosts = @{}
$clusterRestartPendingHosts = @{}
$clusterCheckpointVMCount = @{}
$clusterCheckpointVMList = @()
$clusterRestartPendingVMCount = @{}

foreach ($cluster in $Clusters) {
    $nodes = Get-ClusterNode -Cluster $cluster -ErrorAction SilentlyContinue
    $totalClusterVMs = 0
    $totalClusterHosts = $nodes.Count
    $restartedHostsCount = 0
    $checkpointVMsCount = 0
    $restartPendingHostsCount = 0
    $restartPendingVMCount = 0

    foreach ($node in $nodes) {
        $vms = Get-VM -ComputerName $node.Name -ErrorAction SilentlyContinue
        $VMCount = ($vms | Measure-Object).Count
        $totalClusterVMs += $VMCount
        
        $hostInfo = Get-HostHardwareInfo -HostName $node.Name -VMCount $VMCount -ClusterName $cluster
        if ($hostInfo.RestartedRecent) { $restartedHostsCount++ }
        if ($hostInfo.RestartPending) { $restartPendingHostsCount++ }
        $hostHwList += $hostInfo

        foreach ($vm in $vms) {
            $chkpoints = (Get-VMSnapshot -VMName $vm.Name -ComputerName $node.Name -ErrorAction SilentlyContinue)
            if ($chkpoints) {
                $checkpointVMsCount += 1
                $clusterCheckpointVMList += [PSCustomObject]@{
                    Cluster = $cluster
                    Host = $node.Name
                    VMName = $vm.Name
                    CheckpointCount = $chkpoints.Count
                    CheckpointNames = ($chkpoints | Select-Object -ExpandProperty Name -Unique) -join ", "
                }
            }
            $restartPendingVM = Test-VMRestartPending -VMName $vm.Name -VMHost $node.Name
            if ($restartPendingVM) { $restartPendingVMCount++ }
            $vmFullInfo += Get-VMFullInfo -VM $vm -NodeName $node.Name
        }
    }

    $clusterVMCount[$cluster] = $totalClusterVMs
    $clusterHostCount[$cluster] = $totalClusterHosts
    $clusterRestartedHosts[$cluster] = $restartedHostsCount
    $clusterCheckpointVMCount[$cluster] = $checkpointVMsCount
    $clusterRestartPendingHosts[$cluster] = $restartPendingHostsCount
    $clusterRestartPendingVMCount[$cluster] = $restartPendingVMCount
}

# --- HTML Raporlama ---

$clusterSummaryTable = @"
<h3>Cluster Genel Ozeti</h3>
<table border='1'>
<tr>
<th>Cluster Adı</th><th>Host Sayisi</th><th>Toplam VM Sayisi</th><th>Son 7 Gün Restart Olan Host Sayisi</th><th>Restart Pending Host Sayisi</th><th>Checkpoint Olan VM Sayisi</th><th>Restart Pending VM Sayisi</th>
</tr>
"@
foreach ($cluster in $Clusters) {
    $clusterSummaryTable += "<tr><td>$cluster</td><td>$($clusterHostCount[$cluster])</td><td>$($clusterVMCount[$cluster])</td><td>$($clusterRestartedHosts[$cluster])</td><td>$($clusterRestartPendingHosts[$cluster])</td><td>$($clusterCheckpointVMCount[$cluster])</td><td>$($clusterRestartPendingVMCount[$cluster])</td></tr>"
}
$clusterSummaryTable += "</table>"

$checkpointVmTable = ""
if ($clusterCheckpointVMList.Count -gt 0) {
    $checkpointVmTable = $clusterCheckpointVMList | Select-Object Cluster, Host, VMName, CheckpointCount, CheckpointNames | ConvertTo-Html -PreContent "<h3>Checkpoint Olan VMler Detaylari</h3>" -Fragment
} else {
    $checkpointVmTable = "<h3>Checkpoint Olan VM Yok</h3>"
}

$hwTable = $hostHwList | Select-Object ClusterName, HostName, PhysicalCPU, CoreCount, LogicalCPU, Hyperthreading, MemTotalGB, MemUsedGB, MemFreeGB, VMCount, OSVersion, @{Name="Disk Detayları";Expression={$_.DiskDetails}} | ConvertTo-Html -Fragment -PreContent "<h3>Host Donanım Bilgileri (VM Sayıları ve Disk Detayları)</h3>"

$vmTable = $vmFullInfo | ConvertTo-Html -Fragment -PreContent "<h3>VM Detaylari</h3>"

$HTMLHeader = @"
<style>
    table { border-collapse: collapse; width: 100%; }
    th, td { border: 1px solid #ddd; padding: 6px; }
    th { background-color: #2f4f4f; color: white; }
    tr:nth-child(even) { background-color: #f2f2f2; }
</style>
<h2>Hyper-V Cluster Detayli Rapor - $(Get-Date -Format 'yyyy-MM-dd HH:mm')</h2>
"@

$body = $HTMLHeader + $clusterSummaryTable + "<br><br>" + $checkpointVmTable + "<br><br>" + $hwTable + "<br><br>" + $vmTable

Send-MailMessage -From $MailFrom -To $MailTo -Subject $MailSubject -Body $body -BodyAsHtml -SmtpServer $SMTPServer

Hyper-V altyapısında görünürlük ve otomasyon artık lüks değil, bir gereklilik.
Hazırlanan bu PowerShell script’i ile, “benim ortamımda ne var” sorusu artık dakikalarca konsol gezmeden, tek bir e-posta ile yanıt buluyor. Kritik altyapı ekiplerine zaman, üst yönetime güven, iş sürekliliğine ise sürdürülebilirlik sağlanıyor.

Bu bilgilerin faydalı olması dileğiyle..

İlgili Makaleler

Başa dön tuşu