GPO ile Ortak Klasörleri Domain İçindeki Tüm Client'lara Otomatik Olarak Dağıtma
Active Directory ortamlarında ortak klasörlerin yönetimi, IT ekipleri...
Terminal Services (TS) veya modern adıyla Remote Desktop Services (RDS), kurumsal ortamlarda uzaktan erişim ve merkezi uygulama yönetimi için vazgeçilmez bir teknoloji haline gelmiştir. Ancak, bu güçlü altyapının etkin kullanılabilmesi için kullanılmayan oturumların doğru yönetimi kritik öneme sahiptir. Bu yazıda, terminal sunucularında boşta kalan oturumların neden kapatılması gerektiğini, bunun getirdiği avantajları ve uygulama yöntemlerini detaylıca inceleyeceğiz.
Modern iş dünyasında, özellikle hibrit çalışma modellerinin yaygınlaşmasıyla birlikte, terminal sunucu kullanımı katlanarak artmıştır. Bir terminal sunucuda aynı anda onlarca, hatta yüzlerce kullanıcı oturum açabilir. Bu yoğun kullanım ortamında, her bir oturumun yaşam döngüsünün doğru yönetilmesi, sistemin sağlıklı işleyişi için hayati önem taşır.
Terminal sunucularda açık bırakılan oturumlar, siber güvenlik açısından en kritik risk faktörlerinden biridir. Bir kullanıcı aktif oturumunu kapatmayı unuttuğunda ve çalışma alanından ayrıldığında, ortaya çıkan güvenlik açıkları zincirleme etkilere yol açabilir.
Gerçek Hayat Senaryosu: Muhasebe departmanından bir çalışan, öğle yemeği için bilgisayarından ayrılıyor ancak terminal sunucu oturumunu kilitlemeden bırakıyor. Bu sırada, yetkisiz bir kişi bu açık oturumu kullanarak finansal verilere erişebilir, kritik işlemler yapabilir veya hassas bilgileri kopyalayabilir.
1. Otomatik Kilit Politikaları:
Computer Configuration → Policies → Windows Settings → Security Settings → Local Policies → Security Options
→ Interactive logon: Machine inactivity limit: 900 saniye (15 dakika)
2. Oturum Zaman Aşımı Yapılandırması: Terminal sunucuda Group Policy üzerinden yapılandırma:
Computer Configuration → Administrative Templates → Windows Components → Remote Desktop Services → Remote Desktop Session Host → Session Time Limits
Önerilen ayarlar:
Açık oturumların takibi için PowerShell ile monitoring scripti:
# Aktif RDS oturumlarını listele ve durumlarını kontrol et
Get-RDUserSession | Where-Object {$_.SessionState -eq 'STATE_DISCONNECTED'} |
Select-Object UserName, SessionId, IdleTime, CreateTime |
Export-Csv "C:\Logs\DisconnectedSessions_$(Get-Date -Format 'yyyyMMdd').csv"
# 30 dakikadan uzun süredir boşta olan oturumları tespit et
$IdleThreshold = New-TimeSpan -Minutes 30
Get-RDUserSession | Where-Object {
$_.IdleTime -gt $IdleThreshold -and $_.SessionState -eq 'STATE_ACTIVE'
} | ForEach-Object {
Send-RDUserMessage -HostServer $_.HostServer -UnifiedSessionId $_.SessionId `
-MessageTitle "Güvenlik Uyarısı" `
-MessageBody "Oturumunuz 30 dakikadır boşta. 5 dakika içinde kapatılacaktır."
}
Her terminal sunucu oturumu, sistemin değerli kaynaklarını tüketir. Bu kaynakların etkin yönetimi, sunucu performansını doğrudan etkiler.
Kaynak Tüketimi Analizi:
| Oturum Durumu | Ortalama RAM | CPU Kullanımı | Disk I/O |
|---|---|---|---|
| Aktif Çalışan | 500-800 MB | %5-15 | Yüksek |
| Boşta (Idle) | 200-400 MB | %1-3 | Düşük |
| Disconnected | 100-300 MB | %0-1 | Çok Düşük |
Performance Monitor ile İzleme:
# Performans sayaçlarını oluştur
$CounterList = @(
"\Terminal Services\Active Sessions",
"\Terminal Services\Inactive Sessions",
"\Memory\Available MBytes",
"\Processor(_Total)\% Processor Time"
)
Get-Counter -Counter $CounterList -SampleInterval 60 -MaxSamples 60 |
Export-Counter -Path "C:\PerfLogs\RDSPerformance.blg"
Fair Share CPU Scheduling Aktifleştirme:
Computer Configuration → Administrative Templates → Windows Components →
Remote Desktop Services → Remote Desktop Session Host → Connections
→ Turn on Fair Share CPU Scheduling: Enabled
Bu özellik, CPU kaynaklarını oturumlar arasında adil bir şekilde dağıtır ve tek bir oturumun tüm sistem kaynaklarını tüketmesini engeller.
Terminal Server ortamlarında lisans yönetimi, hem yasal uyumluluk hem de maliyet optimizasyonu açısından kritiktir.
Lisans Durumu Kontrolü:
# RDS Lisans kullanımını kontrol et
Import-Module RemoteDesktopServices
Get-RDLicenseConfiguration | Select-Object Mode, LicenseServer
# Kullanılan ve boştaki lisansları görüntüle
$LicenseInfo = Get-WmiObject Win32_TSLicenseReport -ComputerName "LicenseServer"
$LicenseInfo | Select-Object ProductVersion, TotalLicenses, IssuedLicenses,
@{Name="AvailableLicenses";Expression={$_.TotalLicenses - $_.IssuedLicenses}}
1. Per-User vs Per-Device Lisanslama:
2. Otomatik Lisans Geri Kazanımı:
# 15 gün kullanılmayan lisansları geri al
$StaleDate = (Get-Date).AddDays(-15)
Get-RDLicense | Where-Object {$_.LastUsed -lt $StaleDate} |
ForEach-Object {
Revoke-RDLicense -LicenseId $_.LicenseId
Write-Log "Lisans geri alındı: $($_.UserName)"
}
PowerShell ile özel yönetim aracı geliştirme:
# RDS Yönetim Dashboard
function Show-RDSDashboard {
$Sessions = Get-RDUserSession
$Dashboard = @{
TotalSessions = $Sessions.Count
ActiveSessions = ($Sessions | Where-Object {$_.SessionState -eq 'STATE_ACTIVE'}).Count
DisconnectedSessions = ($Sessions | Where-Object {$_.SessionState -eq 'STATE_DISCONNECTED'}).Count
IdleSessions = ($Sessions | Where-Object {$_.IdleTime -gt (New-TimeSpan -Minutes 15)}).Count
}
# HTML Rapor oluştur
$HTMLReport = @"
<html>
<head>
<title>RDS Dashboard</title>
<style>
body { font-family: Arial; }
.metric {
display: inline-block;
padding: 20px;
margin: 10px;
background: #f0f0f0;
border-radius: 5px;
}
.number { font-size: 36px; color: #2e7d32; }
</style>
</head>
<body>
<h1>Terminal Server Oturum Durumu</h1>
<div class="metric">
<div class="number">$($Dashboard.TotalSessions)</div>
<div>Toplam Oturum</div>
</div>
<div class="metric">
<div class="number">$($Dashboard.ActiveSessions)</div>
<div>Aktif Oturum</div>
</div>
<div class="metric">
<div class="number">$($Dashboard.DisconnectedSessions)</div>
<div>Bağlantısı Kesilmiş</div>
</div>
<div class="metric">
<div class="number">$($Dashboard.IdleSessions)</div>
<div>Boşta Bekleyen</div>
</div>
</body>
</html>
"@
$HTMLReport | Out-File "C:\inetpub\wwwroot\RDSDashboard.html"
}
Zamanlanmış Görev ile Otomatik Temizlik:
# CleanupRDSSessions.ps1
param(
[int]$DisconnectedTimeout = 30, # Dakika
[int]$IdleTimeout = 60, # Dakika
[string]$LogPath = "C:\Logs\RDSCleanup.log"
)
function Write-Log {
param($Message)
"$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')): $Message" |
Add-Content -Path $LogPath
}
# Ana temizlik fonksiyonu
function Start-RDSCleanup {
Write-Log "RDS oturum temizliği başlatıldı"
# Disconnected oturumları kapat
$DisconnectedSessions = Get-RDUserSession |
Where-Object {
$_.SessionState -eq 'STATE_DISCONNECTED' -and
$_.DisconnectTime -lt (Get-Date).AddMinutes(-$DisconnectedTimeout)
}
foreach ($Session in $DisconnectedSessions) {
try {
Invoke-RDUserLogoff -HostServer $Session.HostServer `
-UnifiedSessionId $Session.SessionId -Force
Write-Log "Oturum kapatıldı: $($Session.UserName) - Sebep: Disconnected timeout"
}
catch {
Write-Log "HATA: $($Session.UserName) oturumu kapatılamadı - $_"
}
}
# Idle oturumları kontrol et
$IdleSessions = Get-RDUserSession |
Where-Object {
$_.SessionState -eq 'STATE_ACTIVE' -and
$_.IdleTime -gt (New-TimeSpan -Minutes $IdleTimeout)
}
foreach ($Session in $IdleSessions) {
# Önce uyarı gönder
Send-RDUserMessage -HostServer $Session.HostServer `
-UnifiedSessionId $Session.SessionId `
-MessageTitle "Oturum Kapatılacak" `
-MessageBody "Oturumunuz uzun süredir boşta. 5 dakika içinde kapatılacaktır."
# 5 dakika sonra kapat (başka bir task ile)
Register-ScheduledTask -TaskName "CloseIdleSession_$($Session.SessionId)" `
-Action (New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-Command `"Invoke-RDUserLogoff -HostServer $($Session.HostServer) -UnifiedSessionId $($Session.SessionId) -Force`"") `
-Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5)) `
-RunLevel Highest
}
Write-Log "Temizlik tamamlandı. Kapatılan: $($DisconnectedSessions.Count), Uyarılan: $($IdleSessions.Count)"
}
# Scripti çalıştır
Start-RDSCleanup
# Haftalık oturum analiz raporu
function New-WeeklySessionReport {
$StartDate = (Get-Date).AddDays(-7)
$EndDate = Get-Date
# Event log'larından oturum verilerini topla
$SessionEvents = Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
StartTime = $StartDate
EndTime = $EndDate
}
$Report = [PSCustomObject]@{
Period = "$StartDate - $EndDate"
TotalLogins = ($SessionEvents | Where-Object {$_.Id -eq 21}).Count
TotalLogoffs = ($SessionEvents | Where-Object {$_.Id -eq 23}).Count
UniqueUsers = ($SessionEvents | Select-Object -ExpandProperty UserId -Unique).Count
PeakConcurrentSessions = (Get-Counter "\Terminal Services\Total Sessions" -MaxSamples 10080 -SampleInterval 60 |
Select-Object -ExpandProperty CounterSamples |
Measure-Object -Property CookedValue -Maximum).Maximum
}
# Excel raporu oluştur
$Report | Export-Excel -Path "C:\Reports\WeeklyRDSReport_$(Get-Date -Format 'yyyyMMdd').xlsx" `
-AutoSize -TableName "SessionAnalysis" -ChartType ColumnClustered
}
# Kritik durumlar için e-posta uyarısı
function Send-RDSAlert {
param(
[string]$AlertType,
[string]$Details
)
$EmailParams = @{
To = "it-team@company.com"
From = "rds-monitor@company.com"
Subject = "RDS Alert: $AlertType"
Body = @"
Terminal Server Uyarısı
Tarih/Saat: $(Get-Date)
Sunucu: $env:COMPUTERNAME
Uyarı Tipi: $AlertType
Detaylar:
$Details
Lütfen gerekli kontrolleri yapınız.
"@
SmtpServer = "mail.company.com"
}
Send-MailMessage @EmailParams
}
# İzleme döngüsü
while ($true) {
$Sessions = Get-RDUserSession
# Kritik durum kontrolleri
if ($Sessions.Count -gt 90) {
Send-RDSAlert -AlertType "Yüksek Oturum Sayısı" `
-Details "Aktif oturum sayısı: $($Sessions.Count)/100"
}
$DisconnectedCount = ($Sessions | Where-Object {$_.SessionState -eq 'STATE_DISCONNECTED'}).Count
if ($DisconnectedCount -gt 20) {
Send-RDSAlert -AlertType "Çok Fazla Disconnected Oturum" `
-Details "Disconnected oturum sayısı: $DisconnectedCount"
}
Start-Sleep -Seconds 300 # 5 dakikada bir kontrol
}
Kullanıcı Bazlı Özelleştirme: Farklı kullanıcı grupları için farklı timeout değerleri:
# Departman bazlı oturum politikaları
$Policies = @{
"IT" = @{IdleTimeout = 120; DisconnectTimeout = 60}
"Finance" = @{IdleTimeout = 30; DisconnectTimeout = 15}
"Sales" = @{IdleTimeout = 60; DisconnectTimeout = 30}
}
foreach ($Dept in $Policies.Keys) {
$GPOName = "RDS_Policy_$Dept"
New-GPO -Name $GPOName
Set-GPRegistryValue -Name $GPOName -Key "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" `
-ValueName "MaxIdleTime" -Type DWORD -Value ($Policies[$Dept].IdleTimeout * 60000)
}
Kullanıcılara yönelik otomatik hatırlatıcı sistemi:
# Günlük hatırlatma e-postası
$ReminderHTML = @"
<html>
<body style="font-family: Arial;">
<h2>Terminal Server Kullanım Hatırlatması</h2>
<p>Sayın Kullanıcı,</p>
<p>Terminal sunucu kaynaklarının verimli kullanımı için lütfen:</p>
<ul>
<li>İşiniz bittiğinde oturumunuzu kapatın (Başlat → Oturumu Kapat)</li>
<li>Kısa süreli ayrılmalarda oturumunuzu kilitleyin (Windows + L)</li>
<li>Gereksiz uygulamaları açık bırakmayın</li>
<li>Büyük dosya transferlerini mesai saatleri dışında yapın</li>
</ul>
<p>Teşekkürler,<br>IT Departmanı</p>
</body>
</html>
"@
Send-MailMessage -To "all-users@company.com" -Subject "Terminal Server Kullanım Hatırlatması" `
-Body $ReminderHTML -BodyAsHtml -From "it@company.com"
# Kapasite analizi ve tahmin
function Get-CapacityForecast {
$HistoricalData = Import-Csv "C:\Logs\SessionHistory.csv"
# Trend analizi
$GrowthRate = ($HistoricalData | Measure-Object -Property SessionCount -Average).Average
$CurrentCapacity = 100
$ProjectedUsage = $GrowthRate * 1.2 # %20 güvenlik marjı
if ($ProjectedUsage -gt ($CurrentCapacity * 0.8)) {
Write-Warning "Kapasite artırımı gerekebilir. Tahmini kullanım: $ProjectedUsage/$CurrentCapacity"
# Ölçeklendirme önerisi
$Recommendation = @{
CurrentServers = 2
RecommendedServers = [Math]::Ceiling($ProjectedUsage / 50)
EstimatedCost = [Math]::Ceiling($ProjectedUsage / 50) * 1000
}
return $Recommendation
}
}
# Emergency session cleanup
function Invoke-EmergencyCleanup {
[CmdletBinding(SupportsShouldProcess)]
param(
[switch]$Force,
[string[]]$ExcludeUsers = @("Administrator", "srv_backup")
)
Write-Warning "ACİL DURUM TEMİZLİĞİ BAŞLATILIYOR!"
# Tüm disconnected oturumları kapat
Get-RDUserSession | Where-Object {
$_.SessionState -eq 'STATE_DISCONNECTED' -and
$_.UserName -notin $ExcludeUsers
} | ForEach-Object {
if ($PSCmdlet.ShouldProcess("$($_.UserName)", "Force Logoff")) {
Invoke-RDUserLogoff -HostServer $_.HostServer `
-UnifiedSessionId $_.SessionId -Force
}
}
# Sistem sağlığını kontrol et
$MemoryAvailable = (Get-Counter "\Memory\Available MBytes").CounterSamples.CookedValue
$CPUUsage = (Get-Counter "\Processor(_Total)\% Processor Time").CounterSamples.CookedValue
Write-Output "Temizlik sonrası sistem durumu:"
Write-Output "Kullanılabilir RAM: $MemoryAvailable MB"
Write-Output "CPU Kullanımı: $([Math]::Round($CPUUsage, 2))%"
}
Terminal sunucularında oturum yönetimi, sadece teknik bir gereklilik değil, aynı zamanda kurumsal verimliliği, güvenliği ve kullanıcı deneyimini doğrudan etkileyen stratejik bir konudur. Doğru yapılandırılmış oturum yönetimi politikaları ile:
Bu yazıda ele aldığımız yöntem ve araçları kullanarak, terminal sunucu altyapınızı daha güvenli, verimli ve yönetilebilir hale getirebilirsiniz. Unutmayın ki, her organizasyonun kendine özgü ihtiyaçları vardır. Bu nedenle, burada sunulan çözümleri kendi ortamınıza uyarlayarak ve test ederek uygulamanız önemlidir.
Düzenli izleme, proaktif bakım ve sürekli iyileştirme yaklaşımıyla, terminal sunucu altyapınız kurumunuzun uzaktan çalışma ihtiyaçlarını en üst düzeyde karşılayabilecektir.
Bu makale, Windows Server 2019/2022 RDS ortamları için hazırlanmıştır. PowerShell scriptleri test ortamında denenmelidir.