PowerShell ile Windows Servislerini Yönetme

Windows ortamlarında servis yönetimi, her sysadmin’in günlük rutininin ayrılmaz bir parçası. GUI üzerinden servis konsolunu açıp tek tek tıklamak bir süre sonra hem zaman kaybı hem de hata riski yaratıyor. PowerShell ile bu işlemleri otomatize etmek, toplu yönetim yapmak ve hatta servis sağlığını proaktif olarak izlemek mümkün. Bu yazıda Windows servislerini PowerShell ile yönetmenin tüm inceliklerini, gerçek dünya senaryolarıyla birlikte ele alacağız.

Temel Servis Yönetim Cmdlet’leri

PowerShell’de servis yönetimi için birkaç temel cmdlet var. Bunları iyi bilmek, günlük işlerinizi ciddi ölçüde hızlandırır.

Get-Service ile Servis Bilgisi Almak

Her şey önce durumu görmekle başlar. Get-Service cmdlet’i sisteminizde çalışan, durmuş veya askıya alınmış tüm servisleri listeler.

# Tüm servisleri listele
Get-Service

# Belirli bir servisi sorgula
Get-Service -Name "wuauserv"

# Wildcard ile arama
Get-Service -Name "win*"

# Duruma göre filtrele - sadece çalışan servisler
Get-Service | Where-Object {$_.Status -eq "Running"}

# Sadece durmuş servisler
Get-Service | Where-Object {$_.Status -eq "Stopped"}

Get-Service çıktısında üç temel kolon görürsünüz: Status, Name ve DisplayName. Status değerleri şunlar olabilir:

  • Running: Servis aktif çalışıyor
  • Stopped: Servis durdurulmuş
  • Paused: Servis askıya alınmış
  • StartPending: Servis başlatılıyor
  • StopPending: Servis durduruluyor

Servis Başlatma, Durdurma ve Yeniden Başlatma

# Servisi başlat
Start-Service -Name "wuauserv"

# Servisi durdur
Stop-Service -Name "wuauserv"

# Servisi yeniden başlat
Restart-Service -Name "Spooler"

# Bağımlı servisleri de zorla durdur
Stop-Service -Name "wuauserv" -Force

# Servis durumunu askıya al (servis destekliyorsa)
Suspend-Service -Name "Spooler"

# Askıya alınan servisi devam ettir
Resume-Service -Name "Spooler"

Burada dikkat etmeniz gereken bir nokta var: Stop-Service bazen bağımlı servisler olduğunda hata verir. -Force parametresini kullanmak bağımlı servisleri de durdurur, ama üretim ortamında bunu dikkatli kullanın. Hangi servislerin etkileneceğini önceden kontrol etmek iyi bir alışkanlık.

Servis Yapılandırmasını Değiştirmek

Set-Service ile Servis Ayarlarını Düzenlemek

Set-Service cmdlet’i servis başlangıç tipini, açıklamasını ve görünen adını değiştirmenize olanak tanır.

# Servis başlangıç tipini değiştir
Set-Service -Name "wuauserv" -StartupType Disabled
Set-Service -Name "wuauserv" -StartupType Manual
Set-Service -Name "wuauserv" -StartupType Automatic

# Otomatik (Gecikmeli Başlangıç) için
Set-Service -Name "wuauserv" -StartupType AutomaticDelayedStart

# Servis açıklamasını güncelle
Set-Service -Name "MyService" -Description "Bu servis uygulama X için çalışır"

# Servis görünen adını değiştir
Set-Service -Name "MyService" -DisplayName "Uygulama X Servisi"

StartupType parametresi için geçerli değerler:

  • Automatic: Sistem açılışında otomatik başlar
  • AutomaticDelayedStart: Sistem açılışından sonra gecikmeli başlar
  • Manual: Elle başlatılır
  • Disabled: Devre dışı, başlatılamaz
  • Boot: Sistem yükleyicisi tarafından başlatılır (sürücüler için)
  • System: Kernel başlatması sırasında çalışır

Servis Kimlik Bilgilerini Değiştirmek

Servis hesabını değiştirmeniz gerektiğinde, özellikle domain ortamlarında bu oldukça yaygın bir ihtiyaç:

# Servis çalıştırma hesabını değiştir
$credential = Get-Credential
Set-Service -Name "MyService" -Credential $credential

# Script içinde kullanmak için (şifreyi güvenli şekilde sakla)
$password = ConvertTo-SecureString "P@ssw0rd123" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential("DOMAINserviceaccount", $password)
Set-Service -Name "MyService" -Credential $credential

Prodüksiyonda şifreleri düz metin olarak script’e yazmayın. Bunun yerine Windows Credential Manager veya bir secrets yönetim çözümü kullanın. Bu konuya aşağıda biraz daha değineceğiz.

Uzak Sunucularda Servis Yönetimi

Gerçek sysadmin hayatında onlarca, belki yüzlerce sunucuyu yönetiyorsunuz. Her birine RDP açıp servis konsolunu açmak zaman kaybı. PowerShell’in uzak yönetim kapasitesi burada devreye giriyor.

# Uzak sunucuda tek bir servisin durumunu kontrol et
Get-Service -ComputerName "SERVER01" -Name "wuauserv"

# Birden fazla sunucuda aynı anda kontrol
$servers = @("SERVER01", "SERVER02", "SERVER03", "SERVER04")
Get-Service -ComputerName $servers -Name "Spooler" | 
    Select-Object MachineName, Name, Status | 
    Format-Table -AutoSize

# Uzak sunucuda servis yeniden başlat
Invoke-Command -ComputerName "SERVER01" -ScriptBlock {
    Restart-Service -Name "Spooler" -Force
}

# Birden fazla sunucuda paralel yeniden başlatma
$servers = @("SERVER01", "SERVER02", "SERVER03")
Invoke-Command -ComputerName $servers -ScriptBlock {
    Restart-Service -Name "wuauserv"
    Write-Output "$env:COMPUTERNAME - wuauserv yeniden başlatıldı"
}

-ComputerName parametresini kullanmak için hedef sunucularda WinRM’in aktif olması gerekir. WinRM yapılandırması için domain ortamında Group Policy kullanmak en temiz yol.

Gerçek Dünya Senaryo 1: Toplu Servis Sağlık Kontrolü

Sabah işe geldiniz, uygulamanın bir parçası düzgün çalışmıyor gibi görünüyor. Birden fazla sunucuda ilgili servislerin durumunu hızlıca kontrol etmeniz gerekiyor. İşte bunun için kullanabileceğiniz kapsamlı bir script:

# Çoklu sunucularda servis sağlık raporu oluştur
$servers = @("APP01", "APP02", "APP03", "DB01")
$criticalServices = @("wuauserv", "Spooler", "W32tm", "Dnscache", "LanmanServer")
$results = @()

foreach ($server in $servers) {
    foreach ($service in $criticalServices) {
        try {
            $svc = Get-Service -ComputerName $server -Name $service -ErrorAction Stop
            $results += [PSCustomObject]@{
                Sunucu   = $server
                Servis   = $service
                Durum    = $svc.Status
                Başlangıç = $svc.StartType
                Sağlık   = if ($svc.Status -eq "Running") { "OK" } else { "DIKKAT" }
            }
        }
        catch {
            $results += [PSCustomObject]@{
                Sunucu   = $server
                Servis   = $service
                Durum    = "ERIŞILEMEZ"
                Başlangıç = "Bilinmiyor"
                Sağlık   = "KRITIK"
            }
        }
    }
}

# Sonuçları ekrana yaz
$results | Format-Table -AutoSize

# Sorunlu servisleri filtrele
$problemler = $results | Where-Object { $_.Sağlık -ne "OK" }
if ($problemler) {
    Write-Host "`nDikkat Gerektiren Servisler:" -ForegroundColor Red
    $problemler | Format-Table -AutoSize
}

# HTML rapor olarak kaydet
$results | ConvertTo-Html -Title "Servis Sağlık Raporu" | 
    Out-File "C:ReportsServisRaporu_$(Get-Date -Format 'yyyyMMdd').html"

Bu script’i her sabah otomatik çalıştırmak için Windows Task Scheduler’a ekleyebilirsiniz. Kritik servisler durmuşsa bunu HTML raporda hemen görürsünüz.

Gerçek Dünya Senaryo 2: Otomatik Servis İzleme ve Yeniden Başlatma

Bazı servisler zaman zaman çöküyor ve yeniden başlatmayı bekliyor. Windows’un kendi kurtarma mekanizması var ama PowerShell ile daha özelleştirilebilir bir izleme sistemi kurabilirsiniz:

# Kritik servis izleme ve otomatik kurtarma scripti
param(
    [string]$ServiceName = "MyApplicationService",
    [string]$LogPath = "C:LogsServisIzleme.log",
    [int]$CheckInterval = 60,  # Saniye cinsinden kontrol aralığı
    [int]$MaxRetry = 3
)

function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "[$timestamp] [$Level] $Message"
    Add-Content -Path $LogPath -Value $logEntry
    
    switch ($Level) {
        "ERROR"   { Write-Host $logEntry -ForegroundColor Red }
        "WARNING" { Write-Host $logEntry -ForegroundColor Yellow }
        default   { Write-Host $logEntry }
    }
}

function Test-ServiceHealth {
    param([string]$Name)
    $svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
    return $svc
}

$retryCount = 0
Write-Log "Servis izleme başlatıldı: $ServiceName"

while ($true) {
    $service = Test-ServiceHealth -Name $ServiceName
    
    if ($null -eq $service) {
        Write-Log "Servis bulunamadı: $ServiceName" -Level "ERROR"
        break
    }
    
    if ($service.Status -ne "Running") {
        Write-Log "Servis çalışmıyor! Durum: $($service.Status)" -Level "WARNING"
        
        if ($retryCount -lt $MaxRetry) {
            $retryCount++
            Write-Log "Yeniden başlatma deneniyor... ($retryCount/$MaxRetry)" -Level "WARNING"
            
            try {
                Start-Service -Name $ServiceName -ErrorAction Stop
                Start-Sleep -Seconds 10
                
                $checkService = Get-Service -Name $ServiceName
                if ($checkService.Status -eq "Running") {
                    Write-Log "Servis başarıyla yeniden başlatıldı." 
                    $retryCount = 0
                }
                else {
                    Write-Log "Servis başlatılamadı!" -Level "ERROR"
                }
            }
            catch {
                Write-Log "Hata: $($_.Exception.Message)" -Level "ERROR"
            }
        }
        else {
            Write-Log "Maksimum yeniden deneme sayısına ulaşıldı! Manuel müdahale gerekli." -Level "ERROR"
            # Burada e-posta veya Slack bildirimi gönderilebilir
            break
        }
    }
    else {
        Write-Log "Servis normal çalışıyor."
        $retryCount = 0
    }
    
    Start-Sleep -Seconds $CheckInterval
}

Bu script’i çalıştırmak için: .ServisIzleme.ps1 -ServiceName "Spooler" -CheckInterval 30

Yeni Servis Oluşturma ve Silme

New-Service ile Servis Kaydetmek

Kendi yazdığınız uygulamayı veya bir batch script’i Windows servisi olarak kaydetmek için:

# Temel servis oluşturma
New-Service -Name "MyAppService" `
            -BinaryPathName "C:AppsMyAppmyapp.exe" `
            -DisplayName "Uygulama X Servisi" `
            -Description "Bu servis uygulama X'in arka plan işlemlerini yönetir" `
            -StartupType Automatic

# PowerShell script'ini servis olarak çalıştırma (NSSM ile daha iyi ama native yol)
# Önce NSSM indirin ya da sc.exe kullanın
$binaryPath = "C:WindowsSystem32WindowsPowerShellv1.0powershell.exe -ExecutionPolicy Bypass -File C:ScriptsMyService.ps1"
New-Service -Name "PSService" `
            -BinaryPathName $binaryPath `
            -DisplayName "PowerShell Servis" `
            -StartupType Automatic

# Servisi belirli bir hesapla çalıştır
$password = ConvertTo-SecureString "ServicePass123!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("DOMAINsvcaccount", $password)

New-Service -Name "MySecureService" `
            -BinaryPathName "C:Appsservice.exe" `
            -DisplayName "Güvenli Uygulama Servisi" `
            -Credential $cred `
            -StartupType Automatic

# Servis oluşturduktan sonra başlat
Start-Service -Name "MyAppService"

Servis Silmek

Windows Server 2019 ve PowerShell 6+ ile birlikte Remove-Service cmdlet’i geldi. Daha eski sistemlerde sc.exe kullanmak gerekiyor:

# PowerShell 6+ ve Windows Server 2019+
Remove-Service -Name "MyAppService"

# Eski sistemler için (hala geçerli ve yaygın kullanım)
# Önce servisi durdur
Stop-Service -Name "MyAppService" -Force

# Sonra sil
sc.exe delete "MyAppService"

# Veya WMI üzerinden
$service = Get-WmiObject -Class Win32_Service -Filter "Name='MyAppService'"
$service.Delete()

WMI ile Gelişmiş Servis Bilgisi

Get-Service bazı durumlarda yeterli bilgi vermez. Servisin PID’ini, çalıştırdığı hesabı veya tam yolunu görmek için WMI/CIM kullanmak gerekir:

# CIM ile detaylı servis bilgisi (modern yaklaşım)
Get-CimInstance -ClassName Win32_Service | 
    Where-Object {$_.State -eq "Running"} |
    Select-Object Name, DisplayName, State, StartMode, 
                  ProcessId, StartName, PathName |
    Format-Table -AutoSize

# Belirli bir servisin detaylarını al
Get-CimInstance -ClassName Win32_Service -Filter "Name='Spooler'" |
    Select-Object *

# Yüksek bellek kullanan servislerin PID'lerini bul
$services = Get-CimInstance -ClassName Win32_Service | 
    Where-Object {$_.State -eq "Running" -and $_.ProcessId -gt 0}

foreach ($svc in $services) {
    $process = Get-Process -Id $svc.ProcessId -ErrorAction SilentlyContinue
    if ($process -and $process.WorkingSet64 -gt 500MB) {
        Write-Host "$($svc.Name) - PID: $($svc.ProcessId) - Bellek: $([math]::Round($process.WorkingSet64/1MB, 2)) MB" -ForegroundColor Yellow
    }
}

# Uzak sunucuda CIM sorgusu
$session = New-CimSession -ComputerName "SERVER01"
Get-CimInstance -CimSession $session -ClassName Win32_Service |
    Where-Object {$_.StartMode -eq "Auto" -and $_.State -eq "Stopped"} |
    Select-Object Name, DisplayName, State
Remove-CimSession -CimSession $session

Servis Bağımlılıklarını Yönetmek

Servisler arasındaki bağımlılıkları anlamak, özellikle bakım pencerelerinde servis kapatma sırasını doğru belirlemek için kritik:

# Bir servisin bağımlı olduğu servisleri göster
$service = Get-Service -Name "wuauserv"
$service.RequiredServices

# Bir servise bağımlı servisleri göster
$service.DependentServices

# Detaylı bağımlılık ağacı
function Get-ServiceDependencyTree {
    param([string]$ServiceName, [int]$Level = 0)
    
    $indent = "  " * $Level
    $svc = Get-Service -Name $ServiceName
    Write-Host "$indent$($svc.DisplayName) [$($svc.Status)]"
    
    foreach ($dep in $svc.RequiredServices) {
        Get-ServiceDependencyTree -ServiceName $dep.Name -Level ($Level + 1)
    }
}

Get-ServiceDependencyTree -ServiceName "wuauserv"

İpuçları ve En İyi Pratikler

Uzun yılların getirdiği deneyimle birkaç önemli noktayı paylaşmak isterim:

Hata yönetimini ihmal etmeyin. Servis işlemleri her zaman beklenmedik durumlarla karşılaşabilir. Try-catch bloklarını kullanmak, script’lerinizin sessizce başarısız olmasını önler.

Üretim değişikliklerinde -WhatIf kullanın. Birçok PowerShell cmdlet’i -WhatIf parametresini destekler. Stop-Service -Name "kritikservis" -WhatIf komutu servisi durdurmadan önce ne yapacağını gösterir.

Log tutmayı alışkanlık edinin. Yukarıdaki izleme scriptinde gösterdiğim gibi, tüm kritik işlemleri log dosyasına yazın. Sorun çıktığında bu loglar kurtarıcı olur.

Kimlik bilgilerini güvenli saklayın. Script’lere düz metin şifre yazmak yerine Get-Credential veya Windows Credential Manager kullanın. Domain servis hesapları için gMSA (Group Managed Service Accounts) değerlendirin.

Değişikliklerden önce anlık görüntü alın. Toplu servis yapılandırması değişikliği yapmadan önce mevcut durumu kaydedin:

# Mevcut servis yapılandırmasını yedekle
Get-Service | 
    Select-Object Name, DisplayName, Status, StartType |
    Export-Csv -Path "C:BackupServisYedek_$(Get-Date -Format 'yyyyMMdd_HHmm').csv" -NoTypeInformation -Encoding UTF8

Bu CSV dosyasını bir sorun çıktığında referans olarak kullanabilirsiniz.

Sonuç

PowerShell ile Windows servis yönetimi, GUI’ye kıyasla çok daha güçlü ve esnek bir yapı sunuyor. Tek bir komutla onlarca sunucudaki servislerin durumunu görmek, toplu yapılandırma değişiklikleri yapmak ve otomatik izleme script’leri kurarak proaktif bir yaklaşım benimsemek mümkün. Başlangıçta birkaç temel cmdlet’i ezberlemek yeterli; geri kalanı pratik yaparak geliyor.

Bu yazıda ele aldığımız konuları özetleyecek olursak: temel servis sorgulama ve yönetim işlemleri, yapılandırma değişiklikleri, uzak sunucu yönetimi, otomatik izleme scriptleri, yeni servis oluşturma/silme, WMI ile gelişmiş sorgular ve bağımlılık yönetimi. Bunların hepsini günlük pratiğinize entegre ettiğinizde, servis yönetimi için harcadığınız zamanın önemli ölçüde azaldığını göreceksiniz.

Bir sonraki adım olarak bu script’leri kendi ortamınıza uyarlayın, gerekli sunucu adlarını ve servis isimlerini düzenleyin. Özellikle sağlık kontrolü ve otomatik kurtarma scriptleri, pek çok ortamda gecenin üçünde pager’ı susturmak için biçilmiş kaftan.

Yorum yapın