PowerShell ile CSV ve Excel Dosyası İşleme

Sistem yöneticiliğinde veri işleme kaçınılmaz bir gerçek. Her gün onlarca CSV raporu, kullanıcı listesi, lisans dosyası veya envanter tablosu geliyor masana. Excel’i açıp manuel işlem yapmak yerine PowerShell ile bu işleri otomatize etmek hem zaman kazandırır hem de insan hatası riskini ortadan kaldırır. Bu yazıda CSV ve Excel dosyalarıyla PowerShell üzerinden nasıl etkili şekilde çalışabileceğini, gerçek dünya senaryolarıyla birlikte ele alacağım.

CSV Dosyalarıyla Temel İşlemler

PowerShell, CSV dosyaları için yerleşik cmdlet’lere sahip. Import-Csv ve Export-Csv ikili bu işin bel kemiğini oluşturuyor.

Import-Csv ile Veri Okuma

En temel kullanım şu şekilde:

# Basit CSV okuma
$kullanicilar = Import-Csv -Path "C:Raporlarkullanicilar.csv"
$kullanicilar | Format-Table

# Farklı delimiter kullanan dosyalar için (noktalı virgül gibi)
$veriler = Import-Csv -Path "C:Raporlarenvanter.csv" -Delimiter ";"

# Encoding belirtmek önemli - Türkçe karakter sorunlarını önler
$veriler = Import-Csv -Path "C:Raporlarrapor.csv" -Delimiter ";" -Encoding UTF8

Import-Csv her satırı bir PowerShell objesi olarak yükler. Başlık satırı otomatik olarak property adı olur. Bu sayede nokta notasyonuyla doğrudan erişebilirsin:

# Belirli bir kolona erişim
$kullanicilar = Import-Csv -Path "C:Raporlarkullanicilar.csv" -Encoding UTF8

foreach ($kullanici in $kullanicilar) {
    Write-Host "Ad: $($kullanici.AdSoyad) - Email: $($kullanici.Email) - Departman: $($kullanici.Departman)"
}

# Filtreleme - sadece IT departmanındaki kullanıcılar
$itEkibi = $kullanicilar | Where-Object { $_.Departman -eq "IT" }
Write-Host "IT ekibinde $($itEkibi.Count) kişi var."

# Belirli kolonları seçme
$kullanicilar | Select-Object AdSoyad, Email | Format-Table -AutoSize

Gerçek Dünya Senaryosu 1: Active Directory Toplu Kullanıcı Oluşturma

Klasik sysadmin görevi: İK’dan 50 kişilik işe alım listesi geldi, bunları AD’ye eklemen lazım. CSV’den okuyup toplu oluşturalım.

Önce CSV formatımız şöyle olsun:

  • Ad, Soyad, Departman, Unvan, Manager, OU
# AD'ye toplu kullanıcı ekleme scripti
Import-Module ActiveDirectory

$yeniKullanicilar = Import-Csv -Path "C:HRyeni_personel.csv" -Delimiter ";" -Encoding UTF8

foreach ($kisi in $yeniKullanicilar) {
    $tamAd = "$($kisi.Ad) $($kisi.Soyad)"
    $kullaniciAdi = "$($kisi.Ad.ToLower()).$($kisi.Soyad.ToLower())"
    $email = "[email protected]"
    $varsayilanSifre = ConvertTo-SecureString "Sirket2024!" -AsPlainText -Force

    try {
        New-ADUser `
            -Name $tamAd `
            -GivenName $kisi.Ad `
            -Surname $kisi.Soyad `
            -SamAccountName $kullaniciAdi `
            -UserPrincipalName $email `
            -EmailAddress $email `
            -Department $kisi.Departman `
            -Title $kisi.Unvan `
            -Manager $kisi.Manager `
            -Path $kisi.OU `
            -AccountPassword $varsayilanSifre `
            -ChangePasswordAtLogon $true `
            -Enabled $true

        Write-Host "[OK] $tamAd olusturuldu." -ForegroundColor Green

    } catch {
        Write-Host "[HATA] $tamAd olusturulamadi: $_" -ForegroundColor Red
    }
}

Write-Host "Islem tamamlandi."

Bu script hata yönetimini de içeriyor. Her kullanıcı için ayrı try-catch bloğu kullandık çünkü bir kullanıcıda hata olursa diğerleri etkilenmesin istiyoruz.

Export-Csv ile Veri Dışa Aktarma

# Temel export
$kullanicilar | Export-Csv -Path "C:Raporlarcikti.csv" -Encoding UTF8 -NoTypeInformation

# NoTypeInformation parametresi önemli - olmadan dosyanın başına #TYPE satırı eklenir
# -Append ile mevcut dosyaya ekleme yapabilirsin
$yeniVeri | Export-Csv -Path "C:Raporlarcikti.csv" -Encoding UTF8 -NoTypeInformation -Append

# Sadece belirli kolonları export etmek
Get-ADUser -Filter * -Properties Department, EmailAddress |
    Select-Object Name, SamAccountName, Department, EmailAddress |
    Export-Csv -Path "C:Raporlarad_kullanicilari.csv" -Encoding UTF8 -NoTypeInformation

Gerçek Dünya Senaryosu 2: Disk Kullanım Raporu

Her ay müdüre sunman gereken disk raporu var. Elle yapmak yerine otomatize edelim:

# Sunuculardaki disk kullanımını CSV'ye aktar
$sunucular = @("SRV-WEB01", "SRV-DB01", "SRV-FILE01", "SRV-APP01")
$raporVerisi = @()

foreach ($sunucu in $sunucular) {
    try {
        $diskler = Get-WmiObject -Class Win32_LogicalDisk `
            -ComputerName $sunucu `
            -Filter "DriveType=3" `
            -ErrorAction Stop

        foreach ($disk in $diskler) {
            $toplamGB = [math]::Round($disk.Size / 1GB, 2)
            $bosGB = [math]::Round($disk.FreeSpace / 1GB, 2)
            $kullanilanGB = [math]::Round(($disk.Size - $disk.FreeSpace) / 1GB, 2)
            $kullanilanYuzde = [math]::Round(($kullanilanGB / $toplamGB) * 100, 1)

            $raporVerisi += [PSCustomObject]@{
                Sunucu = $sunucu
                Surucu = $disk.DeviceID
                ToplamGB = $toplamGB
                KullanilanGB = $kullanilanGB
                BosGB = $bosGB
                KullanilanYuzde = "$kullanilanYuzde%"
                RaporTarihi = (Get-Date -Format "yyyy-MM-dd HH:mm")
                Durum = if ($kullanilanYuzde -gt 85) { "KRITIK" } elseif ($kullanilanYuzde -gt 70) { "UYARI" } else { "NORMAL" }
            }
        }
    } catch {
        Write-Warning "$sunucu sunucusuna erisilemedi: $_"
    }
}

$raporAdi = "DiskRaporu_$(Get-Date -Format 'yyyyMMdd').csv"
$raporVerisi | Export-Csv -Path "C:Raporlar$raporAdi" -Encoding UTF8 -NoTypeInformation

# Kritik durumları ekrana da bas
$kritikler = $raporVerisi | Where-Object { $_.Durum -eq "KRITIK" }
if ($kritikler) {
    Write-Host "`nKRITIK disk kullanimi tespit edildi:" -ForegroundColor Red
    $kritikler | Format-Table Sunucu, Surucu, KullanilanYuzde -AutoSize
}

Write-Host "`nRapor olusturuldu: C:Raporlar$raporAdi" -ForegroundColor Green

Excel Dosyalarıyla Çalışmak

CSV’nin aksine Excel dosyaları (.xlsx) PowerShell’de yerleşik destek olmadan işlenemez. Bunun için iki popüler yöntem var.

Yöntem 1: ImportExcel Modülü

ImportExcel modülü, Excel kurulu olmadan .xlsx dosyalarını okuyup yazmanı sağlar. Production ortamında en çok tercih edilen yöntem bu.

# Modülü yükle (bir kez yapman yeterli)
Install-Module -Name ImportExcel -Scope CurrentUser -Force

# Temel Excel okuma
$excelVeri = Import-Excel -Path "C:Raporlarenvanter.xlsx"
$excelVeri | Format-Table -AutoSize

# Belirli bir sheet okuma
$veriler = Import-Excel -Path "C:Raporlarrapor.xlsx" -WorksheetName "Sunucular"

# Başlık satırını atla ve belirli satırdan başla
$veriler = Import-Excel -Path "C:Raporlarrapor.xlsx" -StartRow 3 -HeaderRow 2

Excel’e veri yazma da oldukça kolay:

# Basit Excel export
$raporVerisi | Export-Excel -Path "C:RaporlarSunucuRaporu.xlsx" -WorksheetName "Disk Kullanimi" -AutoSize -AutoFilter

# Birden fazla sheet'e yazma
$adKullanicilari | Export-Excel -Path "C:RaporlarAdRaporu.xlsx" `
    -WorksheetName "AD Kullanicilari" `
    -AutoSize `
    -FreezeTopRow `
    -BoldTopRow

$deaktifKullanicilar | Export-Excel -Path "C:RaporlarAdRaporu.xlsx" `
    -WorksheetName "Deaktif Hesaplar" `
    -AutoSize `
    -FreezeTopRow `
    -BoldTopRow `
    -Append  # Aynı dosyaya farklı sheet eklemek için

Yöntem 2: COM Nesnesi ile Excel Kontrolü

Excel kurulu sistemlerde COM nesnesi üzerinden tam kontrol sağlayabilirsin. Biraz daha karmaşık ama çok daha fazla özelleştirme imkanı sunar.

# COM nesnesi ile Excel işleme
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false  # Arka planda çalıştır
$excel.DisplayAlerts = $false

try {
    $workbook = $excel.Workbooks.Open("C:Raporlarenvanter.xlsx")
    $sheet = $workbook.Worksheets.Item(1)

    # Satır ve kolon sayısını bul
    $sonSatir = $sheet.UsedRange.Rows.Count
    $sonKolon = $sheet.UsedRange.Columns.Count

    Write-Host "Toplam $($sonSatir - 1) kayit, $sonKolon kolon bulundu."

    # Veriyi oku
    $veriler = @()
    for ($satir = 2; $satir -le $sonSatir; $satir++) {
        $veriler += [PSCustomObject]@{
            SunucuAdi = $sheet.Cells.Item($satir, 1).Value2
            IPAdresi = $sheet.Cells.Item($satir, 2).Value2
            IsletimSistemi = $sheet.Cells.Item($satir, 3).Value2
            Departman = $sheet.Cells.Item($satir, 4).Value2
        }
    }

    $workbook.Close($false)

} finally {
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
    [System.GC]::Collect()
}

$veriler | Format-Table -AutoSize

COM nesnesiyle çalışırken finally bloğunu asla atlama. Excel process’i arka planda açık kalabilir ve sonraki çalıştırmada sorun çıkarabilir.

Gerçek Dünya Senaryosu 3: Lisans Takip Sistemi

Şirkette kullanılan yazılım lisanslarını Excel’de takip edip, süresi dolmak üzere olanları otomatik raporlayan bir script:

# ImportExcel modülü gerekli
Import-Module ImportExcel

$bugun = Get-Date
$uyariGunu = 30  # 30 gün içinde dolacakları uyar

# Lisans listesini oku
$lisanslar = Import-Excel -Path "C:Lisanslaryazilim_lisanslari.xlsx" -WorksheetName "Lisanslar"

$raporVerisi = @()
$kritikLisanslar = @()
$uyariLisanslar = @()

foreach ($lisans in $lisanslar) {
    $sonTarih = [datetime]::Parse($lisans.BitisTarihi)
    $kalanGun = ($sonTarih - $bugun).Days

    $durum = switch ($true) {
        ($kalanGun -lt 0)          { "SURESI_DOLMUS" }
        ($kalanGun -le 7)          { "KRITIK" }
        ($kalanGun -le $uyariGunu) { "UYARI" }
        default                    { "AKTIF" }
    }

    $kayit = [PSCustomObject]@{
        Yazilim = $lisans.YazilimAdi
        Tedarikci = $lisans.Tedarikci
        LisansSayisi = $lisans.LisansSayisi
        BitisTarihi = $sonTarih.ToString("dd.MM.yyyy")
        KalanGun = $kalanGun
        Durum = $durum
        SorumluKisi = $lisans.SorumluKisi
    }

    $raporVerisi += $kayit

    if ($durum -in @("KRITIK", "SURESI_DOLMUS")) {
        $kritikLisanslar += $kayit
    } elseif ($durum -eq "UYARI") {
        $uyariLisanslar += $kayit
    }
}

# Excel raporu oluştur
$raporDosya = "C:LisanslarLisansRaporu_$(Get-Date -Format 'yyyyMMdd').xlsx"

# Tüm lisanslar sheet'i
$raporVerisi | Export-Excel -Path $raporDosya `
    -WorksheetName "Tum Lisanslar" `
    -AutoSize `
    -AutoFilter `
    -FreezeTopRow `
    -BoldTopRow

# Kritik lisanslar ayrı sheet'e
if ($kritikLisanslar.Count -gt 0) {
    $kritikLisanslar | Export-Excel -Path $raporDosya `
        -WorksheetName "Kritik" `
        -AutoSize `
        -FreezeTopRow `
        -BoldTopRow `
        -Append
}

Write-Host "Lisans raporu olusturuldu: $raporDosya"
Write-Host "Kritik/Suresi dolmus: $($kritikLisanslar.Count) lisans"
Write-Host "Uyari durumunda: $($uyariLisanslar.Count) lisans"

CSV Dosyası Manipülasyon Teknikleri

Verileri Birleştirme ve Karşılaştırma

İki CSV’yi karşılaştırmak sık karşılaşılan bir senaryo. Örneğin AD’deki kullanıcılarla HR sistemindeki listeyi karşılaştırmak:

# İki CSV'yi karşılaştır - AD'de olup HR'da olmayan kullanıcıları bul
$adKullanicilari = Import-Csv -Path "C:Raporlarad_kullanicilari.csv" -Encoding UTF8
$hrKullanicilari = Import-Csv -Path "C:Raporlarhr_personel.csv" -Encoding UTF8

# HR'da olmayan AD hesaplarını bul (muhtemelen ayrılmış personel)
$adEmail = $adKullanicilari | Select-Object -ExpandProperty Email
$hrEmail = $hrKullanicilari | Select-Object -ExpandProperty KurumsalEmail

$ayrilanPersonel = $adKullanicilari | Where-Object {
    $_.Email -notin $hrEmail -and
    $_.Enabled -eq "True"
}

Write-Host "HR'da kaydı olmayan $($ayrilanPersonel.Count) aktif AD hesabı bulundu:"
$ayrilanPersonel | Select-Object Name, Email, LastLogonDate |
    Export-Csv -Path "C:Raporlarayrilanlar.csv" -Encoding UTF8 -NoTypeInformation

# Join işlemi - iki CSV'den veri birleştir
$birlesikVeri = foreach ($adKullanici in $adKullanicilari) {
    $hrEslesen = $hrKullanicilari | Where-Object { $_.KurumsalEmail -eq $adKullanici.Email }

    [PSCustomObject]@{
        AdSoyad = $adKullanici.Name
        Email = $adKullanici.Email
        ADHesap = $adKullanici.SamAccountName
        HRSicilNo = $hrEslesen.SicilNo
        HRDepartman = $hrEslesen.Departman
        ADDepartman = $adKullanici.Department
        DepartmanUyumu = ($hrEslesen.Departman -eq $adKullanici.Department)
    }
}

$birlesikVeri | Export-Csv -Path "C:Raporlarad_hr_karsilastirma.csv" -Encoding UTF8 -NoTypeInformation

Hata Yönetimi ve Loglama

Production scriptlerinde hata yönetimi kritik. Şu pattern’ı kullanmaya alış:

# CSV işleme scripti için kapsamlı hata yönetimi
$logDosya = "C:Logscsv_islem_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
$hatalar = @()
$basarili = 0
$basarisiz = 0

function Write-Log {
    param($Mesaj, $Seviye = "INFO")
    $satir = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [$Seviye] $Mesaj"
    Add-Content -Path $logDosya -Value $satir -Encoding UTF8
    switch ($Seviye) {
        "ERROR" { Write-Host $satir -ForegroundColor Red }
        "WARN"  { Write-Host $satir -ForegroundColor Yellow }
        "OK"    { Write-Host $satir -ForegroundColor Green }
        default { Write-Host $satir }
    }
}

# CSV'nin var olup olmadığını kontrol et
$csvYolu = "C:Raporlarislenecek.csv"
if (-not (Test-Path $csvYolu)) {
    Write-Log "CSV dosyasi bulunamadi: $csvYolu" "ERROR"
    exit 1
}

$veriler = Import-Csv -Path $csvYolu -Delimiter ";" -Encoding UTF8
Write-Log "Toplam $($veriler.Count) kayit okundu."

foreach ($kayit in $veriler) {
    try {
        # İşlem burada yapılır
        # Örnek: boş alan kontrolü
        if ([string]::IsNullOrWhiteSpace($kayit.Email)) {
            throw "Email alani bos olamaz"
        }

        # Asıl işlem...
        $basarili++
        Write-Log "[$basarili] $($kayit.AdSoyad) islendi." "OK"

    } catch {
        $basarisiz++
        $hatalar += [PSCustomObject]@{
            Kayit = $kayit.AdSoyad
            Hata = $_.Exception.Message
            Zaman = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        }
        Write-Log "$($kayit.AdSoyad) islenirken hata: $_" "ERROR"
    }
}

# Özet rapor
Write-Log "Islem tamamlandi. Basarili: $basarili | Basarisiz: $basarisiz"

if ($hatalar.Count -gt 0) {
    $hatalar | Export-Csv -Path "C:Raporlarhatalar.csv" -Encoding UTF8 -NoTypeInformation
    Write-Log "Hata raporu olusturuldu: C:Raporlarhatalar.csv" "WARN"
}

Performans İpuçları

Büyük dosyalarla çalışırken bazı noktalara dikkat etmek gerekiyor:

  • @() yerine ArrayList kullan: Binlerce satır için $liste = [System.Collections.ArrayList]::new() ve $liste.Add($kayit) yaklaşımı çok daha hızlı çalışır çünkü her += işlemi yeni dizi oluşturur.
  • Pipeline kullan: Mümkün olduğunca Import-Csv | Where-Object | Select-Object | Export-Csv pipeline zincirini kullan, bellek kullanımı düşer.
  • -ReadCount parametresi: Çok büyük dosyalar için Get-Content ile okuyup batch işleme yapabilirsin.
  • Encoding’i her zaman belirt: Özellikle Türkçe karakter içeren dosyalarda -Encoding UTF8 unutulursa büyük baş ağrısı yaşarsın.
  • Test ortamında dene: Production CSV’nin küçük bir kopyasıyla önce test et, ardından gerçek dosyaya geç.

Sonuç

PowerShell ile CSV ve Excel işleme, sysadmin hayatının en pratik araçlarından biri. Günlük raporlama, AD yönetimi, lisans takibi, envanter güncelleme gibi görevler için yazdığın script’ler zamanla birikiyor ve gerçek bir otomasyon altyapısı oluşturuyor.

Başlangıç olarak en basit senaryoyla başla: Bir CSV’yi Import-Csv ile oku, bir şeyler yap, Export-Csv ile yaz. Bu döngüyü kavradıktan sonra hata yönetimi, loglama ve Excel entegrasyonu gibi katmanları ekleyebilirsin. ImportExcel modülü özellikle büyük bir oyun değiştirici, Excel kurulu olmadan sunucularda da çalışabiliyor olması production scriptleri için ideal.

Buradaki script’leri doğrudan kopyala-yapıştır yapma. Kendi ortamına uyarla, test et, üstüne bir şeyler ekle. En iyi öğrenme yöntemi her zaman kendi senaryonla uğraşmak.

Yorum yapın