PowerShell Hataları: Yaygın Sorunlar ve Çözümleri

PowerShell ile ciddi iş yapıyorsanız, hata mesajlarıyla boğuşmak kaçınılmaz. Kırmızı yazılarla dolan bir konsol ekranı karşısında ne yapacağını bilmemek, özellikle production ortamında can sıkıcı bir durum. Bu yazıda en sık karşılaşılan PowerShell hatalarını, neden oluştuklarını ve nasıl çözüleceğini gerçek dünya senaryolarıyla ele alacağız.

PowerShell Hata Türlerini Anlamak

PowerShell’de hatalar iki ana kategoriye ayrılır: Terminating Errors ve Non-Terminating Errors. Bu ayrımı anlamadan hata ayıklama yapmak, karanlıkta el yordamıyla yürümek gibidir.

Terminating Errors script’in tamamen durmasına yol açar. Örneğin yanlış bir cmdlet adı yazmak veya syntax hatası bu kategoriye girer. Non-Terminating Errors ise hata oluşturur ama script çalışmaya devam eder. Bir döngü içinde bazı dosyaları okuyamazsanız, PowerShell hatayı gösterir ama diğer dosyalara geçmeye devam eder.

$Error değişkeni son oturumda oluşan tüm hataları tutar. İlk bakacağınız yer burası olmalı:

# Son hatayı görüntüle
$Error[0]

# Son hatanın detaylı bilgisi
$Error[0] | Format-List * -Force

# Tüm hataları temizle
$Error.Clear()

Execution Policy Hataları

Muhtemelen en çok karşılaşılan ilk engel bu. Yeni bir Windows Server kuruyorsunuz, script’inizi çalıştırmak istiyorsunuz ve şu mesajla karşılaşıyorsunuz:

File cannot be loaded because running scripts is disabled on this system.

Bu hata, execution policy’nin script çalıştırmayı engellediği anlamına gelir. Çözüm basit ama doğru yaklaşımı seçmek önemli:

# Mevcut policy'yi kontrol et
Get-ExecutionPolicy -List

# Sadece mevcut kullanıcı için değiştir (önerilen)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

# Tüm sistem için değiştir (dikkatli kullan)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine

# Script'i bypass ederek tek seferlik çalıştırma
PowerShell -ExecutionPolicy Bypass -File "C:Scriptsmyscript.ps1"

Production ortamında Unrestricted policy’yi asla kullanmayın. RemoteSigned çoğu senaryo için yeterli ve güvenlidir. İnternetten indirilen script’lerin imzalı olmasını zorunlu kılar, yerel script’lere dokunmaz.

Grup politikası ile kısıtlanmış sistemlerde execution policy değiştiremezsiniz. Bu durumda -ExecutionPolicy Bypass parametresi ile çalıştırmak tek seçeneğiniz olabilir, ancak bunu bir task scheduler job’ı olarak ayarlıyorsanız güvenlik ekibinizle konuşun.

“Cannot Find Path” ve Dosya Erişim Hataları

Sysadmin hayatında en sinir bozucu hatalardan biri dosya yolu sorunlarıdır. Özellikle script’leri farklı ortamlar arasında taşırken başınıza gelir:

Cannot find path 'C:Scriptsconfig.xml' because it does not exist.

# Dosya var mı kontrol et, yoksa anlamlı mesaj ver
$configPath = "C:Scriptsconfig.xml"

if (-not (Test-Path -Path $configPath)) {
    Write-Error "Konfigürasyon dosyası bulunamadı: $configPath"
    Write-Host "Lütfen $configPath dosyasını oluşturun veya yolu düzeltin." -ForegroundColor Yellow
    exit 1
}

# UNC path'lerde özel kontrol
$uncPath = "\fileserversharedata"
if (-not (Test-Path -Path $uncPath -ErrorAction SilentlyContinue)) {
    Write-Warning "UNC path erişilemiyor: $uncPath"
}

$PSScriptRoot kullanımı çok kritik. Script’in bulunduğu dizini referans alarak yol sorunlarının büyük bölümünü çözebilirsiniz:

# Script'in kendi dizinini referans al
$scriptDir = $PSScriptRoot
$configPath = Join-Path -Path $scriptDir -ChildPath "config.xml"
$logPath = Join-Path -Path $scriptDir -ChildPath "Logsapp.log"

# Join-Path her zaman tercih edilmeli, string birleştirme değil
# YANLIŞ: $path = $scriptDir + "config.xml"
# DOĞRU:
$path = Join-Path $scriptDir "config.xml"

Dosya kilitleme hataları da sık karşılaşılan bir durum. Bir dosyayı başka bir process açmışsa şu hatayı alırsınız: The process cannot access the file because it is being used by another process.

# Hangi process dosyayı kullanıyor bul
$lockedFile = "C:Logsapp.log"

$processes = Get-Process | Where-Object {
    $_.Modules | Where-Object { $_.FileName -eq $lockedFile }
}

if ($processes) {
    Write-Host "Dosyayı kullanan process'ler:"
    $processes | Select-Object Name, Id, StartTime
}

Modül ve Cmdlet Bulunamadı Hataları

The term 'Get-ADUser' is not recognized as the name of a cmdlet, function, script file, or operable program.

Bu hatayı görünce önce ilgili modülün yüklü olup olmadığını kontrol edin:

# Yüklü modülleri listele
Get-Module -ListAvailable | Where-Object { $_.Name -like "*ActiveDirectory*" }

# Modülü import et
Import-Module ActiveDirectory -ErrorAction Stop

# Modül bulunamazsa yükle (Windows Feature olarak)
Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools

# PowerShell Gallery'den modül yükle
Install-Module -Name Az -Repository PSGallery -Force -AllowClobber

Gerçek dünya senaryosu: Yeni bir yönetici sunucuya bağlanıyor ve Active Directory scriptini çalıştırıyor. Hata alıyor çünkü RSAT araçları o sunucuda yüklü değil. Script’in başına şu kontrolü eklemek iyi bir pratiktir:

# Script başında modül kontrolü
function Test-RequiredModules {
    $requiredModules = @("ActiveDirectory", "DHCPServer")
    
    foreach ($module in $requiredModules) {
        if (-not (Get-Module -ListAvailable -Name $module)) {
            Write-Error "Gerekli modül bulunamadı: $module"
            Write-Host "Yüklemek için: Install-WindowsFeature RSAT-AD-PowerShell" -ForegroundColor Cyan
            return $false
        }
        Import-Module $module -ErrorAction Stop
        Write-Verbose "Modül yüklendi: $module"
    }
    return $true
}

if (-not (Test-RequiredModules)) {
    exit 1
}

Try-Catch ile Profesyonel Hata Yönetimi

Sadece hata almak değil, o hatayı anlamlı şekilde ele almak önemli. Birçok sysadmin script’lerini hata yönetimi olmadan yazar, sonra production’da ne olduğunu anlamaya çalışır.

# Temel try-catch yapısı
try {
    # ErrorAction Stop olmadan try-catch non-terminating hataları yakalamaz
    $result = Get-Item -Path "C:exists_not.txt" -ErrorAction Stop
    Write-Host "Dosya bulundu: $($result.FullName)"
}
catch [System.IO.FileNotFoundException] {
    Write-Error "Dosya bulunamadı: $_"
}
catch [System.UnauthorizedAccessException] {
    Write-Error "Erişim reddedildi. Yönetici olarak çalıştırın."
}
catch {
    # Beklenmedik hatalar için genel yakalayıcı
    Write-Error "Beklenmedik hata: $($_.Exception.Message)"
    Write-Verbose "Stack Trace: $($_.ScriptStackTrace)"
}
finally {
    # Her durumda çalışır (temizlik işlemleri için ideal)
    Write-Host "İşlem tamamlandı (başarılı veya hatalı)"
}

$ErrorActionPreference değişkeni tüm script boyunca varsayılan davranışı belirler. Script başında bunu ayarlamak iyi bir alışkanlık:

# Script genelinde hata davranışını ayarla
$ErrorActionPreference = "Stop"  # Tüm hataları terminating yap

# Belirli bir komut için geçici olarak değiştir
$result = Get-Service -Name "NonExistentService" -ErrorAction SilentlyContinue

# Hata oluştuysa kontrol et
if (-not $result) {
    Write-Warning "Servis bulunamadı, devam ediliyor..."
}

Yetki ve Erişim Hataları

Windows Server ortamında yetki hataları günlük yaşamın parçası. “Access Denied” mesajını görünce panik yapmak yerine sistematik yaklaşın:

# Mevcut kullanıcı ve yetkilerini kontrol et
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
Write-Host "Çalışan kullanıcı: $($currentUser.Name)"

# Administrator olup olmadığını kontrol et
$isAdmin = ([Security.Principal.WindowsPrincipal] $currentUser).IsInRole(
    [Security.Principal.WindowsBuiltinRole]::Administrator
)

if (-not $isAdmin) {
    Write-Warning "Bu script yönetici yetkileri gerektiriyor!"
    Write-Host "PowerShell'i 'Yönetici olarak çalıştır' ile açın." -ForegroundColor Red
    exit 1
}

# Dosya/klasör ACL kontrolü
$path = "C:SecureFolder"
$acl = Get-Acl -Path $path
$acl.Access | Where-Object { $_.IdentityReference -like "*$env:USERNAME*" } |
    Select-Object IdentityReference, FileSystemRights, AccessControlType

Remote PowerShell bağlantılarında WinRM hataları çok yaygın:

Connecting to remote server failed with the following error message: WinRM cannot complete the operation.

# WinRM durumunu kontrol et
Get-Service WinRM | Select-Object Name, Status, StartType

# WinRM'i başlat ve yapılandır
Start-Service WinRM
Set-Service WinRM -StartupType Automatic
Enable-PSRemoting -Force

# TrustedHosts listesini kontrol et ve güncelle
Get-Item WSMan:localhostClientTrustedHosts

# Belirli host ekle
Set-Item WSMan:localhostClientTrustedHosts -Value "server01,server02,192.168.1.*" -Force

# Bağlantıyı test et
Test-WSMan -ComputerName "server01" -ErrorAction Stop

Tip ve Dönüşüm Hataları

PowerShell’in tip sistemi bazen sürprizler yaratır. Özellikle string olarak gelen sayıları işlemeye çalıştığınızda:

Cannot convert value "abc" to type "System.Int32". Error: "Input string was not in a correct format."

# Güvenli tip dönüşümü için TryParse kullan
$inputValue = "123abc"
$parsedInt = 0

if ([int]::TryParse($inputValue, [ref]$parsedInt)) {
    Write-Host "Dönüştürülen değer: $parsedInt"
} else {
    Write-Warning "Geçersiz sayı formatı: $inputValue"
}

# Null kontrolü - PowerShell'de sık hata kaynağı
$obj = $null
if ($null -eq $obj) {  # $obj -eq $null değil! Sol tarafta $null olmalı
    Write-Host "Obje null"
}

# Boş string kontrolü
$str = ""
if ([string]::IsNullOrWhiteSpace($str)) {
    Write-Warning "String boş veya sadece boşluk içeriyor"
}

Loglama ile Hata Takibi

Production ortamında script çalıştırıyorsanız loglama şart. Hata çıktığında ne olduğunu anlamak için log olmadan kör uçuş yapıyorsunuz demektir:

# Kapsamlı loglama fonksiyonu
function Write-Log {
    param(
        [Parameter(Mandatory)]
        [string]$Message,
        
        [ValidateSet("INFO", "WARNING", "ERROR", "DEBUG")]
        [string]$Level = "INFO",
        
        [string]$LogFile = "C:Logsscript_$(Get-Date -Format 'yyyyMMdd').log"
    )
    
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "[$timestamp] [$Level] $Message"
    
    # Konsola renkli yaz
    switch ($Level) {
        "INFO"    { Write-Host $logEntry -ForegroundColor Green }
        "WARNING" { Write-Host $logEntry -ForegroundColor Yellow }
        "ERROR"   { Write-Host $logEntry -ForegroundColor Red }
        "DEBUG"   { Write-Host $logEntry -ForegroundColor Cyan }
    }
    
    # Log dizini yoksa oluştur
    $logDir = Split-Path $LogFile -Parent
    if (-not (Test-Path $logDir)) {
        New-Item -ItemType Directory -Path $logDir -Force | Out-Null
    }
    
    # Dosyaya yaz
    Add-Content -Path $LogFile -Value $logEntry
}

# Kullanım
try {
    Write-Log "Script başlatıldı" -Level "INFO"
    Write-Log "AD kullanıcıları sorgulanıyor..." -Level "DEBUG"
    
    $users = Get-ADUser -Filter * -ErrorAction Stop
    Write-Log "$($users.Count) kullanıcı bulundu" -Level "INFO"
}
catch {
    Write-Log "Kritik hata: $($_.Exception.Message)" -Level "ERROR"
    Write-Log "Hata satırı: $($_.InvocationInfo.ScriptLineNumber)" -Level "ERROR"
}

Script Debugging Teknikleri

Set-PSDebug ve Write-Verbose ikilisi, karmaşık script’leri debug ederken hayat kurtarır:

# Verbose modu aktif et
$VerbosePreference = "Continue"

# Script içinde verbose mesajlar
function Get-ServerInfo {
    [CmdletBinding()]
    param([string]$ComputerName)
    
    Write-Verbose "Sunucu bilgisi alınıyor: $ComputerName"
    
    try {
        $os = Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop
        Write-Verbose "OS bilgisi başarıyla alındı"
        return $os
    }
    catch {
        Write-Verbose "Bağlantı hatası: $($_.Exception.Message)"
        throw
    }
}

# -Verbose parametresiyle çalıştır
Get-ServerInfo -ComputerName "server01" -Verbose

# Breakpoint ile debugging
Set-PSBreakpoint -Script "C:Scriptsmyscript.ps1" -Line 25
Set-PSBreakpoint -Variable "username" -Mode ReadWrite

Transcript özelliği tüm PowerShell oturumunu kayıt altına alır, özellikle sorun tespitinde çok işe yarar:

# Oturumu kaydet
Start-Transcript -Path "C:Logssession_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt" -Append

# İşlemler...
Get-Service | Where-Object { $_.Status -eq "Stopped" }

# Kaydı durdur
Stop-Transcript

Pipeline Hataları ve Null Obje Sorunları

Pipeline’da null değerler beklenmedik sonuçlar doğurur:

# Pipeline'da güvenli null kontrolü
$services = Get-Service -Name "NonExistent*" -ErrorAction SilentlyContinue

# Null veya boş koleksiyon kontrolü
if ($null -eq $services -or @($services).Count -eq 0) {
    Write-Warning "Eşleşen servis bulunamadı"
} else {
    $services | ForEach-Object {
        Write-Host "Servis: $($_.Name) - Durum: $($_.Status)"
    }
}

# ForEach-Object içinde hata yakalama
Get-ChildItem "C:Logs" -Filter "*.log" | ForEach-Object {
    try {
        $content = Get-Content $_.FullName -ErrorAction Stop
        Write-Host "Dosya okundu: $($_.Name), $($content.Count) satır"
    }
    catch {
        Write-Warning "Dosya okunamadı: $($_.Name) - $($_.Exception.Message)"
    }
}

Windows Event Log ile Hata İzleme

Production script’leri için Windows Event Log’a yazmak, merkezi izleme sistemleriyle entegrasyon açısından kritik:

# Event Log kaynağı oluştur (admin gerektirir, bir kez çalıştır)
if (-not [System.Diagnostics.EventLog]::SourceExists("MyPSScript")) {
    New-EventLog -LogName Application -Source "MyPSScript"
}

# Event Log'a yaz
function Write-EventLogEntry {
    param(
        [string]$Message,
        [ValidateSet("Information", "Warning", "Error")]
        [string]$EntryType = "Information",
        [int]$EventId = 1000
    )
    
    Write-EventLog -LogName Application `
                   -Source "MyPSScript" `
                   -EntryType $EntryType `
                   -EventId $EventId `
                   -Message $Message
}

# Kullanım
try {
    # İşlemler
    Write-EventLogEntry -Message "Backup işlemi tamamlandı" -EntryType "Information" -EventId 1001
}
catch {
    Write-EventLogEntry -Message "Backup hatası: $($_.Exception.Message)" `
                        -EntryType "Error" `
                        -EventId 9001
}

Sonuç

PowerShell hata ayıklama becerisi, zamanla ve deneyimle gelişen bir yetenek. Ancak bu yazıda ele aldığımız temel prensipleri benimserseniz çok daha hızlı ilerlersiniz:

  • Her script’te ErrorAction Stop kullanarak hataların sessizce geçmesini engelleyin
  • Try-Catch-Finally bloklarını alışkanlık haline getirin, özellikle dosya ve ağ işlemlerinde
  • Write-Log gibi merkezi loglama fonksiyonları yazın, her script için yeniden icat etmeyin
  • Test-Path ve null kontrollerini agresif kullanın, asla “bu çalışır” diye varsaymayın
  • $PSScriptRoot kullanarak hard-coded path sorunlarını ortadan kaldırın
  • Execution Policy konusunda güvenlik ile kullanılabilirlik dengesini doğru kurun

Hata mesajları düşman değil, rehberdir. Kırmızı konsol ekranı gördüğünüzde $Error[0] | Format-List * -Force ile tam detayı alın, hatanın türüne bakın ve sistematik şekilde ilerleyin. Zamanla bu hataların büyük çoğunluğunu ilk bakışta tanır hale gelirsiniz.

Bir yanıt yazın

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