Onlarca sunucuyu tek tek RDP ile açıp bir şeyler yapmak zorunda kaldığınız oldu mu hiç? Sabahın dördünde kritik bir güncelleme uygulamak için 50 sunucuya bağlanmak, her birinde aynı komutları çalıştırmak… İşte tam bu noktada PowerShell Remoting devreye giriyor ve hayatınızı kökten değiştiriyor.
PSRemoting, Microsoft’un WS-Management protokolü üzerine inşa ettiği, PowerShell komutlarını ve scriptleri uzak bilgisayarlarda çalıştırmanıza olanak tanıyan bir mekanizma. WinRM (Windows Remote Management) servisini arka planda kullanan bu yapı, özellikle büyük Windows Server ortamlarında vazgeçilmez bir araç haline geliyor.
PSRemoting Nasıl Çalışır?
Teknik olarak anlatmak gerekirse, PSRemoting WinRM servisini kullanarak iki uç nokta arasında şifreli bir kanal oluşturuyor. Varsayılan olarak HTTP (port 5985) ve HTTPS (port 5986) üzerinden çalışıyor. Domain ortamlarında Kerberos kimlik doğrulaması kullanılırken, workgroup ortamlarında NTLM veya sertifika tabanlı kimlik doğrulama tercih ediliyor.
Bir bağlantı kurulduğunda, yerel makinenizdeki PowerShell oturumu uzak makinede bir “runspace” oluşturuyor. Yazdığınız komutlar bu runspace içinde çalışıyor ve sonuçlar serileştirilerek size geri gönderiliyor. Bu serileştirme meselesine ileride değineceğiz çünkü bazı sürprizlere yol açabiliyor.
Ortamı Hazırlamak
WinRM Servisini Aktif Etmek
Üretim ortamında PSRemoting’i etkinleştirmeden önce birkaç şeyi netleştirmeniz gerekiyor. Windows Server 2012 ve sonrasında WinRM servisi genellikle çalışıyor olsa da PSRemoting özelliği ayrıca etkinleştirilmesi gerekiyor.
# Yerel makinede PSRemoting'i etkinleştir
Enable-PSRemoting -Force
# Servis durumunu kontrol et
Get-Service WinRM
# WinRM listener'larını listele
winrm enumerate winrm/config/listener
-Force parametresi network profile sorularını atlayarak doğrudan etkinleştirme yapıyor. Özellikle toplu deployment senaryolarında işe yarıyor.
Group Policy ile Merkezi Yönetim
Tek tek sunucularda Enable-PSRemoting çalıştırmak yerine Group Policy kullanmak çok daha mantıklı. GPO yolu şu şekilde:
Computer Configuration > Windows Settings > Security Settings > System Services > Windows Remote Management
Servis başlangıç tipini “Automatic” olarak ayarlayıp, firewall kuralları için de:
Computer Configuration > Windows Settings > Security Settings > Windows Firewall with Advanced Security
üzerinden “Windows Remote Management” kuralını etkinleştirmeniz yeterli.
TrustedHosts Ayarı
Domain dışı makinelere bağlanırken TrustedHosts listesine ekleme yapmak gerekiyor. Domain içindeyseniz bunu atlayabilirsiniz.
# Tek bir host ekle
Set-Item WSMan:localhostClientTrustedHosts -Value "192.168.1.100"
# Birden fazla host ekle
Set-Item WSMan:localhostClientTrustedHosts -Value "server01,server02,192.168.1.*"
# Tüm hostlara izin ver (güvenli ortamlarda)
Set-Item WSMan:localhostClientTrustedHosts -Value "*"
# Mevcut listeye ekleme yap (üzerine yazmadan)
$mevcut = (Get-Item WSMan:localhostClientTrustedHosts).Value
Set-Item WSMan:localhostClientTrustedHosts -Value "$mevcut,yeniserver"
Üretim ortamında wildcard kullanmak yerine spesifik IP veya hostname belirtmeyi tercih edin. Güvenlik açısından her zaman en az ayrıcalık prensibine sadık kalın.
Temel Bağlantı Yöntemleri
Enter-PSSession ile Interaktif Oturum
Tek bir sunucuya bağlanıp interaktif çalışmak istediğinizde Enter-PSSession kullanıyorsunuz. SSH’a benzer bir deneyim sunuyor.
# Domain ortamında basit bağlantı
Enter-PSSession -ComputerName "WEBSERVER01"
# Farklı credentials ile bağlantı
$cred = Get-Credential
Enter-PSSession -ComputerName "192.168.1.50" -Credential $cred
# HTTPS üzerinden bağlantı (sertifika doğrulamasını atla - sadece test için!)
Enter-PSSession -ComputerName "server01" -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)
Bağlandıktan sonra prompt değişiyor: [WEBSERVER01]: PS C:Users...> şeklinde görüyorsunuz. Oturumu kapatmak için Exit-PSSession ya da basitçe exit yazmanız yeterli.
Invoke-Command ile Uzaktan Komut Çalıştırma
Asıl güç Invoke-Command’da yatıyor. Tek bir komutla onlarca sunucuda işlem yapabiliyorsunuz.
# Tek sunucuda komut çalıştır
Invoke-Command -ComputerName "WEBSERVER01" -ScriptBlock { Get-Service -Name "W3SVC" }
# Birden fazla sunucuda paralel çalıştır
$sunucular = @("WEBSERVER01", "WEBSERVER02", "DBSERVER01", "APPSERVER01")
Invoke-Command -ComputerName $sunucular -ScriptBlock {
$hostname = $env:COMPUTERNAME
$disk = Get-PSDrive C | Select-Object Used, Free
[PSCustomObject]@{
Sunucu = $hostname
KullanilanGB = [math]::Round($disk.Used / 1GB, 2)
BosGB = [math]::Round($disk.Free / 1GB, 2)
}
}
Bu örnekte dört sunucudan disk bilgisini paralel olarak çekiyoruz. Sırayla bağlanmak yerine hepsi aynı anda çalışıyor, bu yüzden 4 sunucu yerine tek bir sunucuyla neredeyse aynı sürede tamamlanıyor.
Değişkenleri Uzak Oturuma Taşımak
Yerel değişkenleri uzak oturumda kullanmak istediğinizde $using: scope modifier’ı devreye giriyor. Bu küçük ama kritik bir detay.
# Yerel değişkeni uzakta kullanma
$servisAdi = "Spooler"
$yeniDurum = "Stopped"
Invoke-Command -ComputerName $sunucular -ScriptBlock {
$servis = Get-Service -Name $using:servisAdi
if ($servis.Status -ne $using:yeniDurum) {
Stop-Service -Name $using:servisAdi -Force
Write-Output "$env:COMPUTERNAME: $using:servisAdi servisi durduruldu"
} else {
Write-Output "$env:COMPUTERNAME: $using:servisAdi zaten durmuş"
}
}
$using: olmadan script block içinde yerel değişkenlere erişemezsiniz. Bu konuda başlangıçta hata yapan çok insan var.
Kalıcı Oturumlar (PSSession)
Her Invoke-Command çalıştırdığınızda yeni bir bağlantı açılıp kapanıyor. Defalarca işlem yapacaksanız bu overhead’i ortadan kaldırmak için kalıcı oturumlar kullanın.
# Oturum oluştur
$oturumlar = New-PSSession -ComputerName $sunucular -Credential $cred
# Oluşturulan oturumları listele
Get-PSSession
# Aynı oturumu tekrar kullanarak komut çalıştır
Invoke-Command -Session $oturumlar -ScriptBlock {
Import-Module ActiveDirectory
Get-ADUser -Filter {Enabled -eq $true} | Measure-Object
}
# Başka bir komut gönder (bağlantı tekrar kurulmaz)
Invoke-Command -Session $oturumlar -ScriptBlock {
Get-EventLog -LogName System -Newest 10 -EntryType Error
}
# İşin bitince oturumları kapat
Remove-PSSession -Session $oturumlar
Özellikle döngü içinde birden fazla işlem yapacaksanız bu pattern’i kullanın. Hem performans hem de kaynak kullanımı açısından çok daha verimli.
Gerçek Dünya Senaryoları
Senaryo 1: Toplu Windows Update Durumu Kontrolü
Sabah işe geldiniz, 30 sunucunun Windows Update durumunu rapor etmeniz gerekiyor. Manuel yol saatlerce sürer, PSRemoting ile 2 dakika.
$sunucuListesi = Get-Content "C:sunucular.txt"
$cred = Get-Credential "DOMAINadminkullanici"
$rapor = Invoke-Command -ComputerName $sunucuListesi -Credential $cred -ScriptBlock {
$guncellemeler = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 1
$pendingReboot = $false
# Pending reboot kontrolü
$rebootKey = "HKLM:SOFTWAREMicrosoftWindowsCurrentVersionComponent Based ServicingRebootPending"
if (Test-Path $rebootKey) { $pendingReboot = $true }
[PSCustomObject]@{
Sunucu = $env:COMPUTERNAME
SonGuncelleme = $guncellemeler.InstalledOn
HotFixID = $guncellemeler.HotFixID
YenidenBaslatmaBekliyor = $pendingReboot
OSVersion = (Get-WmiObject Win32_OperatingSystem).Caption
}
} -ErrorAction SilentlyContinue
$rapor | Sort-Object SonGuncelleme | Format-Table -AutoSize
$rapor | Export-Csv "C:Raporlarupdate_raporu_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation -Encoding UTF8
Senaryo 2: IIS Uygulama Havuzlarını Yeniden Başlatmak
Haftalık bakım penceresi geldi, tüm web sunuculardaki belirli uygulama havuzlarını sıfırlamanız gerekiyor.
$webSunuculari = @("WEB01", "WEB02", "WEB03", "WEB04")
$havuzAdi = "MainAppPool"
$sonuclar = Invoke-Command -ComputerName $webSunuculari -ScriptBlock {
param($pool)
Import-Module WebAdministration -ErrorAction SilentlyContinue
try {
$durum = (Get-WebConfiguration "system.applicationHost/applicationPools/add[@name='$pool']").state
if ($durum -eq "Started") {
Restart-WebAppPool -Name $pool
Start-Sleep -Seconds 3
$yeniDurum = (Get-WebConfiguration "system.applicationHost/applicationPools/add[@name='$pool']").state
[PSCustomObject]@{
Sunucu = $env:COMPUTERNAME
HavuzAdi = $pool
EskiDurum = $durum
YeniDurum = $yeniDurum
Sonuc = "Basarili"
}
} else {
[PSCustomObject]@{
Sunucu = $env:COMPUTERNAME
HavuzAdi = $pool
EskiDurum = $durum
YeniDurum = $durum
Sonuc = "Havuz zaten durmus, baslatiliyor"
}
}
}
catch {
[PSCustomObject]@{
Sunucu = $env:COMPUTERNAME
HavuzAdi = $pool
EskiDurum = "Bilinmiyor"
YeniDurum = "Hata"
Sonuc = $_.Exception.Message
}
}
} -ArgumentList $havuzAdi
$sonuclar | Format-Table -AutoSize
Senaryo 3: Disk Doluluk Alarmı
Monitoring sisteminiz çöktü ama disk alarmı almanız gerekiyor. Acil bir scriptla kurtarın durumu.
$tumSunucular = Get-ADComputer -Filter {OperatingSystem -like "*Server*"} |
Select-Object -ExpandProperty Name
$diskRaporu = Invoke-Command -ComputerName $tumSunucular -ScriptBlock {
Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Used -gt 0 } | ForEach-Object {
$dolulukYuzdesi = [math]::Round(($_.Used / ($_.Used + $_.Free)) * 100, 1)
[PSCustomObject]@{
Sunucu = $env:COMPUTERNAME
Surucu = $_.Name
ToplamGB = [math]::Round(($_.Used + $_.Free) / 1GB, 2)
KullanilanGB = [math]::Round($_.Used / 1GB, 2)
BosGB = [math]::Round($_.Free / 1GB, 2)
DolulukYuzdesi = $dolulukYuzdesi
Durum = if ($dolulukYuzdesi -ge 90) { "KRITIK" }
elseif ($dolulukYuzdesi -ge 75) { "UYARI" }
else { "Normal" }
}
}
} -ErrorAction SilentlyContinue
# Sadece sorunlu olanları göster
$kritikler = $diskRaporu | Where-Object { $_.Durum -ne "Normal" } | Sort-Object DolulukYuzdesi -Descending
$kritikler | Format-Table -AutoSize
# E-posta gönder (SMTP ayarlarınızı girin)
if ($kritikler) {
$mesaj = $kritikler | ConvertTo-Html -Fragment | Out-String
Send-MailMessage -To "[email protected]" -From "[email protected]" `
-Subject "UYARI: Disk Doluluk Alarmı" `
-Body $mesaj -BodyAsHtml `
-SmtpServer "mail.sirket.com"
}
Güvenlik Konuları
PSRemoting kullanırken güvenliği ihmal etmek ciddi sonuçlar doğurabilir. Dikkat etmeniz gereken birkaç kritik nokta var.
CredSSP Konusunda Dikkatli Olun: CredSSP kimlik bilgilerini uzak makineye devrediyor, bu da “double-hop” sorununu çözüyor. Ancak pass-the-hash saldırılarına kapı aralıyor. Sadece zorunlu olduğunda kullanın.
JEA (Just Enough Administration): Kullanıcılara tam admin yetkisi vermek yerine sadece ihtiyaçları olan cmdlet’lere erişim sağlayın. Örneğin helpdesk ekibine sadece servis başlatma/durdurma yetkisi verebilirsiniz. Bu konu başlı başına bir yazı gerektiriyor ama varlığından haberdar olun.
Loglama: PSRemoting aktivitelerini loglamak için ScriptBlock Logging ve Module Logging aktif edilmeli. GPO üzerinden şu yolu takip edin:
Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell
HTTPS Kullanın: Üretim ortamında mümkün olduğunda port 5986 üzerinden HTTPS bağlantısını tercih edin. Bunun için WinRM listener’a sertifika bağlamanız gerekiyor.
Sık Karşılaşılan Sorunlar ve Çözümleri
“Access is denied” hatası: Büyük ihtimalle WinRM servisi çalışmıyordur ya da firewall engelliyor. Test-WSMan -ComputerName "sunucu01" ile önce bağlantıyı test edin.
“The WinRM client cannot complete the operation within the time specified” hatası: Network latency veya hedef sunucuda yük sorunu. -SessionOption ile timeout değerini artırın:
$secenek = New-PSSessionOption -OpenTimeout 60000 -OperationTimeout 120000
Invoke-Command -ComputerName "yavasserver" -SessionOption $secenek -ScriptBlock { ... }
Serileştirme sorunu: Uzak makineden dönen objeler “deserialized” hale geliyor ve bazı metodları kaybedebiliyor. Mesela uzakta Get-Process çalıştırıp dönen obje üzerinde Kill() metodu çağıramazsınız. Çözüm: işlemi uzak tarafta tamamlayın, sadece sonucu geri gönderin.
MaxConcurrentUsers limiti: Varsayılan olarak bir makineye aynı anda 5 bağlantı açılabiliyor. Büyük ortamlarda bu yetersiz kalıyor.
# Limiti artır
Set-Item WSMan:localhostShellMaxConcurrentUsers -Value 25
Set-Item WSMan:localhostShellMaxShellsPerUser -Value 30
ThrottleLimit ile paralel bağlantı sayısını kontrol etmek: Invoke-Command varsayılan olarak 32 paralel bağlantı açıyor. Çok fazla sunucu varsa bu kaynak sorununa yol açabilir.
# Paralel bağlantı sayısını sınırla
Invoke-Command -ComputerName $sunucular -ScriptBlock { ... } -ThrottleLimit 10
İpuçları ve Best Practice’ler
Hata yönetimini ihmal etmeyin. Invoke-Command içinde try/catch kullanın. Bir sunucuda hata olduğunda diğerleri etkilenmesin.
Sonuçlara PSComputerName bakın. Invoke-Command’dan dönen her objeye otomatik olarak PSComputerName özelliği ekleniyor. Hangi sonucun hangi sunucudan geldiğini bu şekilde ayırt edebilirsiniz.
-ErrorAction SilentlyContinue kullanımı. Erişilemeyen sunucular için script’inizin duraklamasını istemiyorsanız bu parametreyi ekleyin. Ama kayıp sunucuları da loglamayı unutmayın.
Credential yönetimi için Secret Management modülünü kullanın. Scriptlerde hardcoded şifre kesinlikle olmaz. PowerShell Secret Management modülü ile şifreleri güvenli şekilde saklayın:
# Secret Management modülünü kur
Install-Module Microsoft.PowerShell.SecretManagement
Install-Module Microsoft.PowerShell.SecretStore
# Vault oluştur
Register-SecretVault -Name "SunucuVault" -ModuleName Microsoft.PowerShell.SecretStore
# Credential kaydet
Set-Secret -Name "AdminCred" -Secret (Get-Credential)
# Script içinde kullan
$cred = Get-Secret -Name "AdminCred"
Invoke-Command -ComputerName $sunucular -Credential $cred -ScriptBlock { ... }
Büyük çıktıları yerel filtreleyip gönderin. Uzak sunucudan tüm event log’u çekip yerel filtrelemek yerine filtreyi uzakta yapın. Network trafiğini ciddi ölçüde azaltıyor.
# Kötü yöntem - tüm log çekilip yerel filtreleniyor
Invoke-Command -ComputerName "server01" -ScriptBlock { Get-EventLog -LogName System -Newest 10000 } |
Where-Object { $_.EntryType -eq "Error" }
# İyi yöntem - filtreleme uzakta yapılıyor
Invoke-Command -ComputerName "server01" -ScriptBlock {
Get-EventLog -LogName System -Newest 100 -EntryType Error
}
Sonuç
PSRemoting, Windows Server ortamlarında çalışan bir sysadmin için en temel araçlardan biri. Öğrenme eğrisi var elbette, özellikle serileştirme, scope ve kimlik doğrulama konuları başlangıçta kafa karıştırabiliyor. Ama bir kez kavradıktan sonra “bunu nasıl olmadan yapıyordum” diyorsunuz.
Yapı olarak basit: WinRM servisi çalışıyor, doğru yetkiler var, TrustedHosts veya domain yapılandırması tamam. Bunları bir kere ayarladıktan sonra yüzlerce sunucuyu masanızdan yönetebilirsiniz.
Başlangıç için tavsiyem şu: önce test ortamında deneyin, kalıcı oturumları ve $using: scope modifier’ı kavrayın, credential yönetimini ciddiye alın. Sonrasını kendiniz keşfedersiniz zaten. Bir de JEA konusuna bakmanızı şiddetle tavsiye ederim, ekip büyüdükçe yetki yönetimi hayat kurtarıyor.