PowerShell ile WMI ve CIM Sorguları: Sistem Bilgilerini Yönetmek

Windows ortamında sistem yönetimi yapıyorsanız, er ya da geç WMI ve CIM sorgularıyla yüzleşmek zorunda kalacaksınız. Özellikle büyük ölçekli Windows Server ortamlarında donanım envanteri çıkarmak, servis durumlarını izlemek ya da uzak makinelere bağlanarak anlık bilgi toplamak için bu iki teknoloji adeta olmazsa olmaz. PowerShell bu işleri inanılmaz derecede kolaylaştırıyor, ancak WMI mi kullanmalı CIM mi kullanmalı sorusu hâlâ kafaları karıştırmaya devam ediyor. Bu yazıda her ikisini de detaylıca ele alacağız.

WMI ve CIM Nedir?

WMI (Windows Management Instrumentation), Microsoft’un Windows sistemlerini yönetmek için geliştirdiği bir altyapıdır. 1990’ların sonlarına dayanan bu teknoloji, işletim sistemi bileşenlerine, donanıma, ağ ayarlarına ve uygulama verilerine standart bir arayüz üzerinden erişmenizi sağlar. DCOM (Distributed Component Object Model) protokolü üzerinde çalışır ve bu durum özellikle güvenlik duvarı ayarları açısından bazen baş ağrısı yaratabilir.

CIM (Common Information Model) ise daha modern ve platform bağımsız bir standarttır. Microsoft, PowerShell 3.0 ile birlikte CIM cmdlet’lerini tanıttı ve bunlar WMI’ın yerini almaya başladı. CIM, WSMan (WS-Management) protokolünü kullanır ve bu sayede hem daha güvenli hem de daha az sorun çıkaran bir yapı sunar. Windows Remote Management (WinRM) üzerinden çalışır.

Kısaca özetlemek gerekirse:

  • Get-WmiObject: Eski, DCOM kullanan, PowerShell 3.0 öncesi yöntem
  • Get-CimInstance: Yeni, WSMan kullanan, önerilen modern yöntem

PowerShell 6.0 ve sonrasında Get-WmiObject tamamen kaldırıldı. Bu yüzden yeni scriptlerinizi mutlaka CIM cmdlet’leriyle yazın.

Temel CIM Sorguları

Get-CimInstance ile Başlangıç

En temel kullanım şekliyle bir sisteme ait işlemci bilgisini çekelim:

Get-CimInstance -ClassName Win32_Processor

Bu komut size işlemci adı, çekirdek sayısı, hız ve diğer detayları döndürür. Ama gerçek güç, bu çıktıyı filtrelediğinizde ortaya çıkar. Sadece işlemci adını ve çekirdek sayısını görmek istiyorsanız:

Get-CimInstance -ClassName Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed

Sık Kullanılan WMI/CIM Sınıfları

Günlük işlerinizde en çok kullanacağınız sınıflar şunlardır:

  • Win32_OperatingSystem: İşletim sistemi bilgileri
  • Win32_Processor: İşlemci detayları
  • Win32_PhysicalMemory: Fiziksel RAM bilgisi
  • Win32_LogicalDisk: Mantıksal disk bilgileri
  • Win32_NetworkAdapterConfiguration: Ağ kartı yapılandırması
  • Win32_Service: Windows servisleri
  • Win32_Process: Çalışan işlemler
  • Win32_BIOS: BIOS bilgileri
  • Win32_ComputerSystem: Genel sistem bilgileri

Pratik Senaryolar ve Kod Örnekleri

Senaryo 1: Disk Kullanım Raporu

Bir müşteri ortamında 50’den fazla sunucunun disk kullanımını takip etmek zorunda kaldım. Manuel kontrol imkânsız, bu yüzden şu scripti geliştirdim:

$sunucular = @("SUNUCU01", "SUNUCU02", "SUNUCU03")

foreach ($sunucu in $sunucular) {
    $diskler = Get-CimInstance -ClassName Win32_LogicalDisk `
        -ComputerName $sunucu `
        -Filter "DriveType=3"
    
    foreach ($disk in $diskler) {
        $toplamGB = [math]::Round($disk.Size / 1GB, 2)
        $bosGB = [math]::Round($disk.FreeSpace / 1GB, 2)
        $kullanilanYuzde = [math]::Round((($disk.Size - $disk.FreeSpace) / $disk.Size) * 100, 1)
        
        [PSCustomObject]@{
            Sunucu          = $sunucu
            DiskHarfi       = $disk.DeviceID
            ToplamGB        = $toplamGB
            BosGB           = $bosGB
            KullanilanYuzde = "$kullanilanYuzde%"
            Durum           = if ($kullanilanYuzde -gt 85) { "KRITIK" } elseif ($kullanilanYuzde -gt 70) { "UYARI" } else { "NORMAL" }
        }
    }
} | Sort-Object KullanilanYuzde -Descending | Format-Table -AutoSize

Bu script kritik diskleri hemen üste sıralıyor ve görsel olarak durumu net şekilde gösteriyor. Eğer e-posta bildirimi de eklemek istiyorsanız Durum -eq "KRITIK" olan satırları filtreleyip Send-MailMessage ile gönderebilirsiniz.

Senaryo 2: Bellek Kullanımı ve Toplam RAM

$bellekBilgisi = Get-CimInstance -ClassName Win32_OperatingSystem

$toplamRAM = [math]::Round($bellekBilgisi.TotalVisibleMemorySize / 1MB, 2)
$bosRAM = [math]::Round($bellekBilgisi.FreePhysicalMemory / 1MB, 2)
$kullanilanRAM = $toplamRAM - $bosRAM
$kullanilanYuzde = [math]::Round(($kullanilanRAM / $toplamRAM) * 100, 1)

Write-Host "Toplam RAM: $toplamRAM GB"
Write-Host "Kullanilan RAM: $kullanilanRAM GB"
Write-Host "Bos RAM: $bosRAM GB"
Write-Host "Kullanim Yuzdesi: %$kullanilanYuzde"

# Fiziksel RAM modüllerini de listeleyelim
Get-CimInstance -ClassName Win32_PhysicalMemory | 
    Select-Object BankLabel, Manufacturer, 
        @{N="KapasiteGB"; E={[math]::Round($_.Capacity / 1GB, 0)}},
        Speed, MemoryType

Senaryo 3: WQL Sorguları ile Filtreleme

CIM ve WMI, SQL’e benzeyen WQL (WMI Query Language) destekler. Bu sayede çok daha hedefli sorgular yazabilirsiniz:

# Sadece çalışan servisleri listele
$calisanServisler = Get-CimInstance -Query "SELECT * FROM Win32_Service WHERE State = 'Running' AND StartMode = 'Auto'"

# Durmuş ama otomatik başlaması gereken servisleri bul (kritik bir kontrol!)
$sorunluServisler = Get-CimInstance -Query "SELECT * FROM Win32_Service WHERE State = 'Stopped' AND StartMode = 'Auto' AND Name != 'gupdate'"

if ($sorunluServisler) {
    Write-Warning "Asagidaki servisler durmus durumda:"
    $sorunluServisler | Select-Object Name, DisplayName, State, StartMode | Format-Table -AutoSize
} else {
    Write-Host "Tum otomatik servisler calisiyor." -ForegroundColor Green
}

Bu sorgu production ortamında benim için sayısız kez hayat kurtardı. Özellikle Windows Update veya bir restart sonrasında bazı servislerin ayağa kalkmadığını tespit etmek için mükemmel.

Uzak Bilgisayarlara Bağlanmak: CimSession

CIM’in en güçlü özelliklerinden biri CimSession desteğidir. Birden fazla sunucuya aynı anda bağlanıp toplu sorgu yapabilirsiniz:

# Birden fazla sunucuya CimSession aç
$sunucular = @("SUNUCU01", "SUNUCU02", "SUNUCU03", "SUNUCU04")

$oturumlar = New-CimSession -ComputerName $sunucular -Credential (Get-Credential)

# Tüm sunucularda aynı anda işletim sistemi bilgisi al
$osSonuclari = Get-CimInstance -CimSession $oturumlar -ClassName Win32_OperatingSystem |
    Select-Object PSComputerName, Caption, Version, 
        @{N="UptimeGun"; E={[math]::Round((Get-Date - $_.LastBootUpTime).TotalDays, 1)}},
        @{N="SonRestart"; E={$_.LastBootUpTime}}

$osSonuclari | Sort-Object UptimeGun -Descending | Format-Table -AutoSize

# Oturumları kapat (önemli!)
Remove-CimSession -CimSession $oturumlar

Önemli not: CimSession’ları kullandıktan sonra mutlaka kapatın. Açık oturumlar sunucu kaynaklarını tüketir ve uzun süreli scriptlerde bağlantı limitine takılabilirsiniz.

WMI Namespace’leri Keşfetmek

WMI sadece Win32 sınıflarından ibaret değil. Farklı namespace’ler altında yüzlerce sınıf mevcut:

# Mevcut namespace'leri listele
Get-CimInstance -Namespace root -ClassName __Namespace | 
    Select-Object Name | Sort-Object Name

# Belirli bir namespace altındaki sınıfları bul
Get-CimClass -Namespace root/cimv2 | 
    Where-Object {$_.CimClassName -like "Win32_Net*"} |
    Select-Object CimClassName

# Bir sınıfın tüm özelliklerini keşfet
Get-CimClass -ClassName Win32_NetworkAdapterConfiguration |
    Select-Object -ExpandProperty CimClassProperties |
    Select-Object Name, CimType |
    Sort-Object Name

Bu keşif komutları özellikle yeni bir sınıfla çalışmaya başlarken çok işe yarıyor. Hangi özelliklerin mevcut olduğunu bilmeden doğru sorgu yazmak zorlaşıyor.

Donanım Envanteri Script’i

Gerçek bir production senaryosu: Yeni bir müşteri aldınız ve onların Windows Server envanterini çıkarmanız gerekiyor. Şu script birkaç dakikada eksiksiz bir envanter oluşturur:

function Get-SunucuEnvanter {
    param(
        [Parameter(Mandatory)]
        [string[]]$SunucuListesi,
        [string]$CiktiDosyasi = "envanter_$(Get-Date -Format 'yyyyMMdd').csv"
    )
    
    $sonuclar = @()
    
    foreach ($sunucu in $SunucuListesi) {
        Write-Host "Isleniyor: $sunucu" -ForegroundColor Cyan
        
        try {
            $oturum = New-CimSession -ComputerName $sunucu -ErrorAction Stop
            
            $os = Get-CimInstance -CimSession $oturum -ClassName Win32_OperatingSystem
            $cpu = Get-CimInstance -CimSession $oturum -ClassName Win32_Processor | Select-Object -First 1
            $sistem = Get-CimInstance -CimSession $oturum -ClassName Win32_ComputerSystem
            $bios = Get-CimInstance -CimSession $oturum -ClassName Win32_BIOS
            
            $toplamDisk = Get-CimInstance -CimSession $oturum -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
                Measure-Object Size -Sum | Select-Object -ExpandProperty Sum
            
            $sonuclar += [PSCustomObject]@{
                SunucuAdi       = $sunucu
                OSVersiyon      = $os.Caption
                OSBuild         = $os.BuildNumber
                CPUModel        = $cpu.Name
                CPUCekirdek     = $cpu.NumberOfCores
                RAMtoplamGB     = [math]::Round($sistem.TotalPhysicalMemory / 1GB, 0)
                ToplamDiskGB    = [math]::Round($toplamDisk / 1GB, 0)
                Manufacturer    = $sistem.Manufacturer
                Model           = $sistem.Model
                SerialNo        = $bios.SerialNumber
                SonRestart      = $os.LastBootUpTime
                UptimeGun       = [math]::Round((Get-Date - $os.LastBootUpTime).TotalDays, 0)
            }
            
            Remove-CimSession -CimSession $oturum
        }
        catch {
            Write-Warning "$sunucu baglanilamadi: $($_.Exception.Message)"
            $sonuclar += [PSCustomObject]@{
                SunucuAdi = $sunucu
                OSVersiyon = "ERISIM HATASI"
            }
        }
    }
    
    $sonuclar | Export-Csv -Path $CiktiDosyasi -NoTypeInformation -Encoding UTF8
    Write-Host "`nEnvanter kaydedildi: $CiktiDosyasi" -ForegroundColor Green
    return $sonuclar
}

# Kullanim ornegi
$sunucular = Get-Content "sunucu_listesi.txt"
Get-SunucuEnvanter -SunucuListesi $sunucular

CIM Yöntemi Çağırma (Invoke-CimMethod)

CIM sadece bilgi okumak için değil, işlem yapmak için de kullanılabilir. Servis başlatma, durdurma veya süreç öldürme gibi işlemleri de CIM üzerinden yapabilirsiniz:

# Bir servisi CIM üzerinden yeniden başlat
$servis = Get-CimInstance -ClassName Win32_Service -Filter "Name='Spooler'"
Invoke-CimMethod -InputObject $servis -MethodName StopService
Start-Sleep -Seconds 3
Invoke-CimMethod -InputObject $servis -MethodName StartService

# Uzak sunucuda işlem sonlandır
$uzakIslem = Get-CimInstance -ClassName Win32_Process `
    -ComputerName "SUNUCU01" `
    -Filter "Name='notepad.exe'"

if ($uzakIslem) {
    Invoke-CimMethod -InputObject $uzakIslem -MethodName Terminate
    Write-Host "Islem sonlandirildi."
}

WMI Event Subscription ile Monitoring

WMI’ın az bilinen ama çok güçlü bir özelliği de event subscription’dır. Belirli olaylar gerçekleştiğinde otomatik tepki verebilirsiniz:

# Yeni bir süreç başladığında bildirim al
$sorgu = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'"

Register-CimIndicationEvent `
    -Query $sorgu `
    -SourceIdentifier "YeniSurecIzleyici" `
    -Action {
        $surecAdi = $Event.SourceEventArgs.NewEvent.TargetInstance.Name
        $pid = $Event.SourceEventArgs.NewEvent.TargetInstance.ProcessId
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - Yeni surec: $surecAdi (PID: $pid)" -ForegroundColor Yellow
    }

Write-Host "Izleme basladi. Durdurmak icin Ctrl+C basin..."
Write-Host "Aboneyi kaldirmak icin: Unregister-Event -SourceIdentifier 'YeniSurecIzleyici'"

# Test et
# Start-Process notepad

# Izlemeyi durdur
# Unregister-Event -SourceIdentifier "YeniSurecIzleyici"

Bu tekniği zararlı yazılım tespiti veya beklenmedik süreç başlatmalarını loglamak için kullanabilirsiniz.

Performans ve Optimizasyon İpuçları

Büyük ortamlarda CIM sorgularını hızlandırmak için birkaç kritik nokta var:

Sadece Gerekli Özellikleri Çekin

# Yavaş - tüm özellikleri çeker
Get-CimInstance -ClassName Win32_Process

# Hızlı - sadece gerekli özellikleri çeker
Get-CimInstance -ClassName Win32_Process -Property Name, ProcessId, WorkingSetSize

-Property parametresi ağ trafiğini ve işlem süresini dramatik şekilde azaltır. 50 sunucuya sorguda bu fark dakikalar mertebesine ulaşabilir.

Filter Parametresini Kullanın

# Kötü pratik - tüm veriyi çekip PowerShell'de filtrele
Get-CimInstance -ClassName Win32_Service | Where-Object {$_.State -eq "Stopped"}

# İyi pratik - WMI tarafında filtrele, daha az veri transferi
Get-CimInstance -ClassName Win32_Service -Filter "State='Stopped'"

Filtrelemeyi her zaman WMI/CIM tarafında yapın. Where-Object kullanmak veriyi önce tam olarak çekip sonra filtrelemek anlamına gelir.

Paralel Sorgu için Workflow veya Jobs

# Birden fazla sunucuya paralel sorgu (PowerShell 7+)
$sunucular = @("SUNUCU01", "SUNUCU02", "SUNUCU03", "SUNUCU04", "SUNUCU05")

$sonuclar = $sunucular | ForEach-Object -Parallel {
    $sunucu = $_
    try {
        $disk = Get-CimInstance -ComputerName $sunucu -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'" -ErrorAction Stop
        [PSCustomObject]@{
            Sunucu    = $sunucu
            BosGB     = [math]::Round($disk.FreeSpace / 1GB, 1)
            ToplamGB  = [math]::Round($disk.Size / 1GB, 1)
            Durum     = "OK"
        }
    } catch {
        [PSCustomObject]@{
            Sunucu = $sunucu
            Durum  = "HATA: $($_.Exception.Message)"
        }
    }
} -ThrottleLimit 10

$sonuclar | Format-Table -AutoSize

Yaygın Hatalar ve Çözümleri

DCOM/RPC bağlantı hataları: Eski Get-WmiObject kullanıyorsanız ve uzak bağlantıda sorun yaşıyorsanız, büyük ihtimalle güvenlik duvarında 135 numaralı port veya dinamik RPC portları kapalıdır. CIM’e geçerseniz sadece 5985 (HTTP) veya 5986 (HTTPS) portuna ihtiyaç duyarsınız.

WinRM servisi başlatılmamış: CIM uzak bağlantı için WinRM gerektirir. Hızlı çözüm için hedef sunucuda Enable-PSRemoting -Force komutunu çalıştırın.

Yetersiz yetki hataları: CIM sorgularını çalıştırmak için hedef makinede local admin ya da Remote Management Users grubunda üyelik gereklidir. Domain ortamında GPO ile bu izinleri merkezi yönetebilirsiniz.

Timeout sorunları: Yavaş ağ bağlantılarında veya meşgul sunucularda zaman aşımı yaşayabilirsiniz:

$oturumSec = New-CimSessionOption -OperationTimeoutSec 30
$oturum = New-CimSession -ComputerName "UZAK-SUNUCU" -SessionOption $oturumSec

Sonuç

WMI ve CIM, Windows sistem yöneticisinin araç kutusundaki en güçlü aletlerden ikisi. Eski projelerde Get-WmiObject görseniz de yeni yazdığınız her şeyi mutlaka Get-CimInstance ve CimSession ile yazın; hem daha hızlı hem daha güvenli hem de PowerShell 7+ uyumlu olur.

Bu yazıda ele aldığımız konuları özetlemek gerekirse: temel CIM sorgularından başlayıp WQL filtreleme, uzak bağlantı yönetimi, donanım envanteri, event subscription ve performans optimizasyonuna kadar oldukça geniş bir yelpazede ilerledik. Gerçek production ortamlarında en çok işinize yarayacak senaryoları gösterdim, ancak bu buzdağının sadece görünen kısmı.

CIM sınıflarını keşfetmeye devam edin. Get-CimClass -Namespace root/cimv2 | Where-Object {$_.CimClassName -like "Win32_*"} komutuyla 800’den fazla sınıf görürsünüz ve bunların büyük çoğunluğu gerçek dünya problemlerine çözüm üretmek için orada bekliyor. İyi scriptlemeler!

Yorum yapın