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..










Elinize sağlık.
Eline sağlık.
Teşekkür ederim.