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-Csvpipeline zincirini kullan, bellek kullanımı düşer. -ReadCountparametresi: Çok büyük dosyalar içinGet-Contentile okuyup batch işleme yapabilirsin.- Encoding’i her zaman belirt: Özellikle Türkçe karakter içeren dosyalarda
-Encoding UTF8unutulursa 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.