PowerShell ile Hyper-V Sanal Makine Yönetimi

Hyper-V ortamlarını GUI üzerinden yönetmek başlangıçta kullanışlı görünse de, onlarca sanal makineyle uğraşmaya başladığınızda fare tıklamalarının ne kadar zaman kaybettirdiğini acı bir şekilde fark ediyorsunuz. PowerShell bu noktada devreye giriyor ve tekrarlayan işlemleri otomatize etmenizi, toplu değişiklikler yapmanızı ve tüm ortamınızı kod üzerinden yönetmenizi sağlıyor. Bu yazıda Hyper-V yönetiminin PowerShell tarafını gerçek dünya senaryolarıyla ele alacağız.

Başlamadan Önce: Ortam Hazırlığı

PowerShell ile Hyper-V yönetmeye başlamadan önce gerekli modülün yüklü olduğundan emin olmanız gerekiyor. Windows Server’da bu modül genellikle Hyper-V rolüyle birlikte geliyor, ancak bazı durumlarda manuel kurulum gerekebiliyor.

# Hyper-V PowerShell modülünü kontrol et
Get-WindowsFeature -Name Hyper-V-PowerShell

# Eksikse yükle (Server Core dahil)
Install-WindowsFeature -Name Hyper-V-PowerShell

# Mevcut Hyper-V komutlarını listele
Get-Command -Module Hyper-V | Measure-Object

Uzak bir Hyper-V sunucusunu yönetecekseniz, WinRM’nin yapılandırılmış olması ve gerekli izinlerin verilmiş olması gerekiyor. Yetkisiz erişim denemelerinde karşılaşacağınız “Access Denied” hataları çoğunlukla bu adımın atlanmasından kaynaklanıyor.

# Uzak sunucuya bağlanarak VM listesini çek
$sunucu = "HYPERV-PROD-01"
Get-VM -ComputerName $sunucu

# Credential ile bağlanma
$cred = Get-Credential
Get-VM -ComputerName $sunucu -Credential $cred

Sanal Makine Listeleme ve Durum Kontrolü

Günlük operasyonlarda en sık kullandığım komutlardan biri Get-VM. Basit görünse de filtreleme özellikleriyle oldukça güçlü bir araç.

# Tüm VM'leri listele
Get-VM

# Sadece çalışan VM'leri göster
Get-VM | Where-Object {$_.State -eq "Running"}

# Belirli bir VM'nin detaylarını gör
Get-VM -Name "WebServer-01" | Format-List *

# CPU ve Memory kullanımını göster
Get-VM | Select-Object Name, State, 
    @{N="CPU(%)";E={$_.CPUUsage}},
    @{N="RAM(MB)";E={$_.MemoryAssigned/1MB}} | 
    Format-Table -AutoSize

# Birden fazla host üzerindeki tüm VM'leri toplu sorgula
$hostlar = @("HYPERV-01", "HYPERV-02", "HYPERV-03")
Get-VM -ComputerName $hostlar | 
    Select-Object ComputerName, Name, State, CPUUsage |
    Sort-Object ComputerName, Name

Ürün ortamında birden fazla Hyper-V node’unuz varsa ve hepsinin durumunu tek seferde görmek istiyorsanız, bu toplu sorgulama yaklaşımı hayat kurtarıyor. Ben bunu sabah rutinimin bir parçası olarak otomatik çalıştırıyorum.

Sanal Makine Oluşturma

Yeni bir VM oluşturmak için New-VM komutunu kullanıyoruz. Asgari parametrelerle çalışabilse de, prodüksiyon ortamında her şeyi açıkça belirtmek iyi bir alışkanlık.

# Temel VM oluşturma
New-VM -Name "AppServer-05" `
       -MemoryStartupBytes 4GB `
       -Generation 2 `
       -Path "D:VirtualMachines" `
       -NewVHDPath "D:VirtualMachinesAppServer-05AppServer-05.vhdx" `
       -NewVHDSizeBytes 80GB `
       -SwitchName "External-Switch"

# VM oluşturduktan sonra ek yapılandırma
$vmName = "AppServer-05"

# CPU sayısını ayarla
Set-VM -Name $vmName -ProcessorCount 4

# Dynamic Memory yapılandır
Set-VMMemory -VMName $vmName `
             -DynamicMemoryEnabled $true `
             -MinimumBytes 2GB `
             -StartupBytes 4GB `
             -MaximumBytes 16GB

# DVD sürücüsüne ISO bağla
Add-VMDvdDrive -VMName $vmName
Set-VMDvdDrive -VMName $vmName `
               -Path "C:ISOsWindows_Server_2022.iso"

# VM'i başlat
Start-VM -Name $vmName

Write-Host "$vmName başarıyla oluşturuldu ve başlatıldı." -ForegroundColor Green

Bu akışı bir şablon haline getirip, farklı VM tipleri için parametre dosyaları oluşturmak oldukça pratik. Özellikle aynı konfigürasyonda onlarca VM deploy etmeniz gerektiğinde bu yaklaşım saatlerce zaman kazandırıyor.

Snapshot (Checkpoint) Yönetimi

Snapshot yönetimi Hyper-V ortamlarında kritik bir konu. Doğru kullanıldığında çok değerli, yanlış kullanıldığında disk alanını hızla tüketen bir özellik.

# Belirli bir VM için snapshot oluştur
$vmName = "WebServer-01"
$snapshotAdi = "Guncelleme_Oncesi_$(Get-Date -Format 'yyyyMMdd_HHmm')"

Checkpoint-VM -Name $vmName -SnapshotName $snapshotAdi
Write-Host "Checkpoint oluşturuldu: $snapshotAdi"

# Tüm snapshot'ları listele
Get-VMSnapshot -VMName $vmName | 
    Select-Object Name, CreationTime, ParentSnapshotName |
    Format-Table -AutoSize

# Belirli bir snapshot'a geri dön
$hedefSnapshot = Get-VMSnapshot -VMName $vmName -Name "Guncelleme_Oncesi_20241115_0930"
Restore-VMSnapshot -VMSnapshot $hedefSnapshot -Confirm:$false

# Eski snapshot'ları temizle (30 günden eski olanlar)
$eskiSnapshots = Get-VMSnapshot -VMName $vmName | 
    Where-Object {$_.CreationTime -lt (Get-Date).AddDays(-30)}

foreach ($snapshot in $eskiSnapshots) {
    Write-Host "Siliniyor: $($snapshot.Name) - $($snapshot.CreationTime)"
    Remove-VMSnapshot -VMSnapshot $snapshot -Confirm:$false
}

Gerçek dünya notu: Prod ortamında snapshot temizleme işlemini tam olarak anlamamış junior ekip üyeleri bazen tüm snapshot zincirini silip VM’i bozabiliyor. Script’e onay mekanizması eklemek veya sadece belirli bir yaşın üzerindeki snapshot’ları hedeflemek bu riski azaltıyor.

Disk Yönetimi

Sanal disklerin yönetimi sysadmin hayatının önemli bir parçası. Disk ekleme, boyutlandırma ve VHD/VHDX dönüşümleri gibi işlemleri PowerShell ile kolayca halledebilirsiniz.

# VM'e yeni disk ekle
$vmName = "DBServer-01"
$diskYolu = "E:VirtualMachinesDBServer-01Data_Disk.vhdx"

# Yeni VHDX oluştur
New-VHD -Path $diskYolu -SizeBytes 500GB -Dynamic

# VM'e bağla
Add-VMHardDiskDrive -VMName $vmName `
                    -Path $diskYolu `
                    -ControllerType SCSI

# Mevcut diskleri listele
Get-VMHardDiskDrive -VMName $vmName | 
    Select-Object Name, Path, ControllerType, ControllerNumber, ControllerLocation

# Disk boyutunu genişlet (VM kapalıyken veya çevrimiçi genişletme için)
$vhd = Get-VHD -Path $diskYolu
Write-Host "Mevcut boyut: $([math]::Round($vhd.Size/1GB, 2)) GB"

Resize-VHD -Path $diskYolu -SizeBytes 1TB
Write-Host "Yeni boyut: $([math]::Round((Get-VHD -Path $diskYolu).Size/1GB, 2)) GB"

# VHD dosyasının gerçek kullanımını kontrol et
Get-VHD -Path $diskYolu | Select-Object Path, 
    @{N="Boyut(GB)";E={[math]::Round($_.Size/1GB,2)}},
    @{N="KullanilanAlan(GB)";E={[math]::Round($_.FileSize/1GB,2)}},
    VhdFormat, VhdType

Ağ Yapılandırması

Virtual Switch ve Network Adapter yönetimi, özellikle VLAN segmentasyonu yapıyorsanız, dikkat gerektiren bir alan.

# Mevcut Virtual Switch'leri listele
Get-VMSwitch | Select-Object Name, SwitchType, NetAdapterInterfaceDescription

# Yeni External Switch oluştur
New-VMSwitch -Name "Prod-External" `
             -NetAdapterName "Ethernet0" `
             -AllowManagementOS $true

# Internal switch oluştur (lab ortamları için ideal)
New-VMSwitch -Name "Lab-Internal" -SwitchType Internal

# VM'e network adapter ekle
Add-VMNetworkAdapter -VMName "WebServer-01" `
                     -SwitchName "Prod-External" `
                     -Name "Management-NIC"

# VLAN yapılandırması
Set-VMNetworkAdapterVlan -VMName "WebServer-01" `
                         -VMNetworkAdapterName "Management-NIC" `
                         -Access `
                         -VlanId 100

# Bandwidth limiti ayarla (Mbps cinsinden, bytes/saniye olarak girilmeli)
Set-VMNetworkAdapter -VMName "WebServer-01" `
                     -Name "Management-NIC" `
                     -MaximumBandwidth 1000000000  # 1 Gbps

# Tüm VM'lerin network adapter bilgilerini toplu çek
Get-VM | ForEach-Object {
    $vm = $_
    Get-VMNetworkAdapter -VMName $vm.Name | 
        Select-Object @{N="VM";E={$vm.Name}}, 
                      Name, SwitchName, MacAddress,
                      @{N="IP";E={$_.IPAddresses -join ", "}}
} | Format-Table -AutoSize

Toplu VM Yönetimi – Gerçek Dünya Senaryosu

Bir güncelleme penceresi öncesinde tüm VM’lerin snapshot alınması, güncellemeler bittikten sonra snapshot’ların temizlenmesi gibi senaryolar oldukça yaygın. Bu tür operasyonlar için hazır scriptler bulundurmak sizi büyük bir kalabalıktan kurtarır.

# Güncelleme öncesi toplu snapshot scripti
param(
    [Parameter(Mandatory=$true)]
    [string]$ComputerName,
    
    [string]$SnapshotPrefix = "PreUpdate",
    
    [switch]$ExcludeOff
)

$tarih = Get-Date -Format "yyyyMMdd_HHmm"
$log = @()

try {
    $vmler = Get-VM -ComputerName $ComputerName

    if ($ExcludeOff) {
        $vmler = $vmler | Where-Object {$_.State -eq "Running"}
        Write-Host "Sadece çalışan VM'ler hedefleniyor: $($vmler.Count) adet" -ForegroundColor Yellow
    }

    foreach ($vm in $vmler) {
        $snapshotAdi = "${SnapshotPrefix}_${tarih}"
        
        try {
            Checkpoint-VM -Name $vm.Name `
                          -ComputerName $ComputerName `
                          -SnapshotName $snapshotAdi `
                          -ErrorAction Stop
            
            $log += [PSCustomObject]@{
                VM = $vm.Name
                Snapshot = $snapshotAdi
                Durum = "Basarili"
                Zaman = Get-Date
            }
            Write-Host "OK: $($vm.Name)" -ForegroundColor Green
        }
        catch {
            $log += [PSCustomObject]@{
                VM = $vm.Name
                Snapshot = "N/A"
                Durum = "HATA: $_"
                Zaman = Get-Date
            }
            Write-Host "HATA: $($vm.Name) - $_" -ForegroundColor Red
        }
    }
}
finally {
    # Log dosyasına yaz
    $logDosyasi = "C:LogsVMSnapshot_$tarih.csv"
    $log | Export-Csv -Path $logDosyasi -NoTypeInformation -Encoding UTF8
    Write-Host "`nLog kaydedildi: $logDosyasi" -ForegroundColor Cyan
    
    # Özet
    $basarili = ($log | Where-Object {$_.Durum -eq "Basarili"}).Count
    $hatali = ($log | Where-Object {$_.Durum -ne "Basarili"}).Count
    Write-Host "`nÖzet - Basarili: $basarili | Hatali: $hatali"
}

Bu scripti çalıştırmak için:

# Basit kullanım
.TopluSnapshot.ps1 -ComputerName "HYPERV-PROD-01"

# Sadece çalışan VM'ler için
.TopluSnapshot.ps1 -ComputerName "HYPERV-PROD-01" -ExcludeOff -SnapshotPrefix "Pazar_Bakimi"

VM Live Migration

Cluster ortamında VM’leri node’lar arasında taşımak hem bakım penceresi yönetimi hem de load balancing için gerekiyor.

# Tek VM'i başka host'a taşı (Live Migration)
Move-VM -Name "WebServer-01" `
        -DestinationHost "HYPERV-02" `
        -IncludeStorage `
        -DestinationStoragePath "D:VirtualMachines"

# Bir host üzerindeki tüm VM'leri başka bir host'a taşı
# (bakım modu için ideal)
$kaynak = "HYPERV-01"
$hedef = "HYPERV-02"

$vmler = Get-VM -ComputerName $kaynak | Where-Object {$_.State -eq "Running"}
Write-Host "$kaynak üzerinde $($vmler.Count) çalışan VM bulundu."

foreach ($vm in $vmler) {
    Write-Host "Tasiniyor: $($vm.Name)..." -NoNewline
    try {
        Move-VM -Name $vm.Name `
                -ComputerName $kaynak `
                -DestinationHost $hedef `
                -ErrorAction Stop
        Write-Host " TAMAM" -ForegroundColor Green
    }
    catch {
        Write-Host " HATA: $_" -ForegroundColor Red
    }
}

Dikkat: IncludeStorage parametresi diskleri de taşıdığı için network üzerinde ciddi yük oluşturabilir. Sadece cluster shared storage kullanıyorsanız bu parametreye gerek yok.

Performans İzleme

Hangi VM’in kaynak tükettiğini bulmak, kapasite planlaması için kritik bilgi sağlıyor.

# Anlık performans görüntüsü
function Get-VMPerformans {
    param(
        [string]$ComputerName = $env:COMPUTERNAME,
        [int]$OrnekSayisi = 5,
        [int]$AralikSaniye = 2
    )
    
    $sonuclar = @()
    
    for ($i = 1; $i -le $OrnekSayisi; $i++) {
        Write-Progress -Activity "Performans verisi toplanıyor" `
                       -Status "Örnek $i / $OrnekSayisi" `
                       -PercentComplete (($i/$OrnekSayisi)*100)
        
        $vmler = Get-VM -ComputerName $ComputerName | 
                 Where-Object {$_.State -eq "Running"}
        
        foreach ($vm in $vmler) {
            $sonuclar += [PSCustomObject]@{
                Zaman = Get-Date
                VM = $vm.Name
                CPU_Yuzde = $vm.CPUUsage
                RAM_MB = [math]::Round($vm.MemoryAssigned/1MB, 0)
                Durum = $vm.State
            }
        }
        
        if ($i -lt $OrnekSayisi) { Start-Sleep -Seconds $AralikSaniye }
    }
    
    # Ortalama değerleri hesapla
    $sonuclar | Group-Object VM | ForEach-Object {
        [PSCustomObject]@{
            VM = $_.Name
            OrtCPU = [math]::Round(($_.Group.CPU_Yuzde | Measure-Object -Average).Average, 2)
            MaxCPU = ($_.Group.CPU_Yuzde | Measure-Object -Maximum).Maximum
            OrtRAM_MB = [math]::Round(($_.Group.RAM_MB | Measure-Object -Average).Average, 0)
        }
    } | Sort-Object OrtCPU -Descending
}

# Fonksiyonu çalıştır
Get-VMPerformans -ComputerName "HYPERV-PROD-01" -OrnekSayisi 10 -AralikSaniye 3 | 
    Format-Table -AutoSize

Yedekleme Entegrasyonu

Hyper-V’de export işlemi basit bir yedekleme yöntemi olarak kullanılabilir. Kritik VM’ler için düzenli export almak en kötü senaryolarda bile size bir geri dönüş noktası sağlıyor.

# VM Export script'i
param(
    [string]$VMName,
    [string]$ExportYolu = "\NAS-01BackupsHyper-V",
    [int]$SaklamaGunu = 7
)

$tarih = Get-Date -Format "yyyyMMdd"
$hedefDizin = Join-Path $ExportYolu "$VMName$tarih"

# Dizin oluştur
if (-not (Test-Path $hedefDizin)) {
    New-Item -Path $hedefDizin -ItemType Directory -Force | Out-Null
}

Write-Host "Export başlıyor: $VMName -> $hedefDizin"
$baslangic = Get-Date

try {
    Export-VM -Name $VMName -Path $hedefDizin -ErrorAction Stop
    
    $sure = (Get-Date) - $baslangic
    $boyut = (Get-ChildItem $hedefDizin -Recurse | 
              Measure-Object -Property Length -Sum).Sum
    
    Write-Host "Export tamamlandi!" -ForegroundColor Green
    Write-Host "Sure: $([math]::Round($sure.TotalMinutes, 1)) dakika"
    Write-Host "Boyut: $([math]::Round($boyut/1GB, 2)) GB"
}
catch {
    Write-Host "Export HATASI: $_" -ForegroundColor Red
    exit 1
}

# Eski yedekleri temizle
$eskiYedekler = Get-ChildItem (Join-Path $ExportYolu $VMName) -Directory |
    Where-Object {$_.CreationTime -lt (Get-Date).AddDays(-$SaklamaGunu)}

foreach ($eski in $eskiYedekler) {
    Write-Host "Eski yedek siliniyor: $($eski.Name)"
    Remove-Item $eski.FullName -Recurse -Force
}

Faydalı Parametreler ve Komutlar

Günlük kullanımda işe yarayan bazı ek komutlar:

  • Stop-VM -Name "VM" -Force: VM’i zorla kapatır, misafir OS’u beklemez
  • Suspend-VM -Name "VM": VM’i RAM’i koruyarak duraklatar
  • Resume-VM -Name "VM": Duraklatılmış VM’i devam ettirir
  • Get-VMIntegrationService -VMName "VM": Integration Services durumunu gösterir
  • Enable-VMIntegrationService -VMName "VM" -Name "Guest Service Interface": Belirli bir servis aktif eder
  • Compare-VM: VM’i başka bir host’a import etmeden önce uyumluluk kontrol eder
  • Measure-VM -VMName "VM": Resource Metering verilerini çeker (aktifse)
  • Set-VMProcessor -VMName "VM" -CompatibilityForMigrationEnabled $true: Migration için CPU uyumluluğunu etkinleştirir

Sonuç

PowerShell ile Hyper-V yönetimi, başlangıçta birkaç basit komut öğrenmekle başlasa da zamanla tüm altyapınızı kod olarak yönetebileceğiniz bir noktaya ulaşıyor. Günlük operasyonlarda zaman kazanmak, insan hatalarını azaltmak ve tekrarlanabilir süreçler oluşturmak için bu araçları kullanmak artık opsiyonel değil, zorunlu.

En önemli tavsiyem, küçük başlamak. Önce Get-VM ile ortamınızı keşfedin, sonra basit otomasyonlar yazın. Zamanla snapshot yönetimi, migration ve yedekleme gibi kritik işlemleri de script’lerinize ekleyebilirsiniz. Tüm script’lerinizi versiyon kontrolüne (Git) alın ve production’da test etmeden önce mutlaka lab ortamında deneyin.

Herhangi bir sorunla karşılaşırsanız Get-Help -Full ile kapsamlı dokümantasyona ulaşabilirsiniz. Hyper-V modülünün Microsoft tarafından sürekli güncellenen resmi dokümantasyonu da her zaman güvenilir bir kaynak olmaya devam ediyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir