Veriler kaybolduğunda “keşke yedek alsaydım” demek, sysadmin kariyerinin en acı dersidir. Bu dersi bir kez yaşayan kimse bir daha aynı hatayı yapmaz ama maalesef çoğu zaman iş işten geçmiş olur. Windows Server ortamlarında yedekleme stratejisi kurmak, sadece bir checkbox işaretlemek değil, gerçek anlamda düşünülmüş, test edilmiş ve güvenli depolamayı da kapsayan bir mimari kurgulamaktır. Bu yazıda hem teorik altyapıyı hem de günlük hayatta kullanabileceğiniz pratik PowerShell betiklerini ele alacağız.
Yedekleme Stratejisinin Temelleri
Yedekleme dünyasında herkesin bildiği ama çoğunun tam uygulamadığı 3-2-1 kuralı vardır. Üç kopya veri, iki farklı medya türü, bir kopya offsite. Kulağa basit gelir ama şirket ortamlarında bu kuralın kaç adımının atlandığını görünce şaşırırsınız.
Windows Server ortamında yedekleme stratejisi kurarken şu temel kavramları netleştirmemiz gerekiyor:
- RPO (Recovery Point Objective): Bir felaket anında kabul edebileceğiniz maksimum veri kaybı süresi. “Son 4 saatin verisini kaybedebiliriz” diyorsanız RPO 4 saattir.
- RTO (Recovery Time Objective): Sistemin tekrar ayağa kalkması için kabul edilebilir maksimum süre. “4 saat içinde sistemi döndürmemiz gerekiyor” diyorsanız RTO 4 saattir.
- Tam Yedek (Full Backup): Tüm verinin kopyalanması. Yer kaplar ama geri dönüş en temizdir.
- Artımlı Yedek (Incremental Backup): Son yedekten bu yana değişen verilerin kopyalanması. Hızlıdır ama geri yükleme zinciri gerektirir.
- Diferansiyel Yedek (Differential Backup): Son tam yedekten bu yana değişen tüm verilerin kopyalanması. İkisi arasında bir denge sunar.
Windows Server Backup ile Temel Yedekleme
Windows Server Backup, işletim sisteminin içinde gelen ücretsiz araçtır. Küçük ve orta ölçekli ortamlar için gayet kullanışlıdır. Önce feature kurulumunu yapalım.
# Windows Server Backup özelliğini yükle
Install-WindowsFeature -Name Windows-Server-Backup -IncludeManagementTools
# Yükleme durumunu doğrula
Get-WindowsFeature -Name Windows-Server-Backup
Feature kurulduktan sonra komut satırından wbadmin aracıyla yedekleme yapabilirsiniz. GUI’ye güvenmek yerine betik ile çalışmayı alışkanlık haline getirin çünkü GUI’yi otomatize edemezsiniz.
# Tüm kritik birimlerin yedeğini al, E: sürücüsüne kaydet
wbadmin start backup -backupTarget:E: -include:C: -allCritical -quiet
# Sistem durumu yedeği al (Active Directory için kritik)
wbadmin start systemstatebackup -backupTarget:E: -quiet
# Mevcut yedekleme versiyonlarını listele
wbadmin get versions -backupTarget:E:
Burada dikkat edilmesi gereken nokta şu: -allCritical parametresi sistem durumunu, önyükleme dosyalarını ve işletim sistemi birimini otomatik dahil eder. Domain Controller ortamlarında mutlaka sistem durumu yedeği alın, sadece dosya yedeği almak AD kurtarma için yeterli olmayacaktır.
PowerShell ile Otomatik Yedekleme Betiği
Elle çalıştırılan yedeklemeler er ya da geç unutulur veya atlanır. Aşağıdaki betik hem yedekleme alır hem de sonucu log dosyasına yazar, hatalarda e-posta bildirimi gönderir.
# Windows-Backup-Auto.ps1
# Çalıştırmadan önce $smtpServer ve $emailTo değişkenlerini ayarlayın
param(
[string]$BackupTarget = "\BACKUP-SRVBackups$(hostname)",
[string]$LogPath = "C:LogsBackup",
[string]$EmailTo = "[email protected]",
[string]$SmtpServer = "smtp.sirket.com"
)
# Log dizinini oluştur
if (-not (Test-Path $LogPath)) {
New-Item -ItemType Directory -Path $LogPath -Force | Out-Null
}
$LogFile = "$LogPathbackup_$(Get-Date -Format 'yyyyMMdd_HHmm').log"
$StartTime = Get-Date
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogEntry = "[$Timestamp] [$Level] $Message"
Write-Host $LogEntry
Add-Content -Path $LogFile -Value $LogEntry
}
Write-Log "Yedekleme basliyor. Hedef: $BackupTarget"
# Yedekleme hedef dizinini oluştur
$TargetPath = "$BackupTarget$(Get-Date -Format 'yyyyMMdd')"
if (-not (Test-Path $TargetPath)) {
New-Item -ItemType Directory -Path $TargetPath -Force | Out-Null
}
# Yedekleme işlemini çalıştır
try {
$Result = wbadmin start backup -backupTarget:$TargetPath `
-include:C: -allCritical -quiet 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log "Yedekleme basariyla tamamlandi."
$Status = "BASARILI"
} else {
Write-Log "Yedekleme hatasi olustu. Cikis kodu: $LASTEXITCODE" "ERROR"
$Status = "BASARISIZ"
}
} catch {
Write-Log "Kritik hata: $($_.Exception.Message)" "ERROR"
$Status = "KRITIK HATA"
}
$EndTime = Get-Date
$Duration = $EndTime - $StartTime
Write-Log "Sure: $($Duration.TotalMinutes.ToString('F2')) dakika"
# E-posta bildirimi gönder
$Subject = "[$Status] $(hostname) Yedekleme Raporu - $(Get-Date -Format 'dd.MM.yyyy')"
$Body = Get-Content $LogFile | Out-String
try {
Send-MailMessage -To $EmailTo -From "[email protected]" `
-Subject $Subject -Body $Body `
-SmtpServer $SmtpServer -Encoding UTF8
Write-Log "E-posta bildirimi gonderildi."
} catch {
Write-Log "E-posta gonderilemedi: $($_.Exception.Message)" "WARNING"
}
Bu betiği Task Scheduler’a ekleyerek gece otomatik çalıştırabilirsiniz. Task Scheduler’da SYSTEM hesabıyla çalıştırmayı ve “Run whether user is logged on or not” seçeneğini işaretlemeyi unutmayın.
Eski Yedekleri Otomatik Temizleme
Disk dolunca yedekleme durur. Bu yüzden eski yedekleri periyodik olarak temizleyen bir mekanizma kurmanız şart. Aşağıdaki betik belirtilen günden eski yedekleri siler.
# Cleanup-OldBackups.ps1
# 30 günden eski yedekleri temizler
param(
[string]$BackupRoot = "\BACKUP-SRVBackups",
[int]$RetentionDays = 30,
[string]$LogFile = "C:LogsBackupcleanup.log"
)
function Write-Log {
param([string]$Message)
$Entry = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Message"
Write-Host $Entry
Add-Content -Path $LogFile -Value $Entry
}
$CutoffDate = (Get-Date).AddDays(-$RetentionDays)
Write-Log "Temizlik basladi. $RetentionDays gundan eski yedekler silinecek."
Write-Log "Kesim tarihi: $($CutoffDate.ToString('dd.MM.yyyy'))"
$DeletedCount = 0
$FreedSpace = 0
Get-ChildItem -Path $BackupRoot -Directory | Where-Object {
# Klasor adini tarih olarak parse etmeye calis
$FolderDate = $null
if ([DateTime]::TryParseExact($_.Name, "yyyyMMdd",
[System.Globalization.CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::None, [ref]$FolderDate)) {
$FolderDate -lt $CutoffDate
}
} | ForEach-Object {
$FolderSize = (Get-ChildItem -Path $_.FullName -Recurse -File |
Measure-Object -Property Length -Sum).Sum
Write-Log "Siliniyor: $($_.FullName) ($('{0:F2}' -f ($FolderSize/1GB)) GB)"
Remove-Item -Path $_.FullName -Recurse -Force
$DeletedCount++
$FreedSpace += $FolderSize
}
Write-Log "Temizlik tamamlandi. $DeletedCount klasor silindi."
Write-Log "Toplam kazanilan alan: $('{0:F2}' -f ($FreedSpace/1GB)) GB"
Güvenli Depolama: Şifreleme ve Erişim Kontrolü
Yedek aldınız, güzel. Peki bu yedek yetkisiz kişilerin eline geçerse ne olur? Yedekler genellikle production sistemlerden daha az korunan ortamlarda tutulur. Bu ciddi bir güvenlik açığıdır.
Yedekleme Hedefine Erişim Kısıtlama
Yedekleme sunucusuna sadece gerekli hesapların erişmesi gerekir. Birkaç temel kural:
- Yedekleme için özel bir servis hesabı oluşturun, Domain Admin kullanmayın
- Bu hesabın sadece yedekleme hedef klasörüne yazma yetkisi olsun
- Yedek dosyalarını okuyan hesap ayrı olsun, yedek alan hesap ayrı olsun
- Yedek sunucusunda RDP erişimini minimumda tutun
# Yedekleme servis hesabı oluştur ve hakları ver
# Active Directory ortamında çalıştırın
# Servis hesabı oluştur
New-ADUser -Name "svc_backup" `
-SamAccountName "svc_backup" `
-UserPrincipalName "[email protected]" `
-AccountPassword (ConvertTo-SecureString "G#k9mP@backup2024!" -AsPlainText -Force) `
-PasswordNeverExpires $true `
-CannotChangePassword $true `
-Enabled $true `
-Description "Yedekleme servis hesabi - Elle degistirme"
# Hesaba yedekleme hakkı ver (Backup Operators grubuna ekle)
Add-ADGroupMember -Identity "Backup Operators" -Members "svc_backup"
# Yedek klasörüne NTFS izinlerini ayarla
$BackupPath = "E:Backups"
$ACL = Get-Acl $BackupPath
# Tüm mevcut izinleri temizle (inheritance dahil)
$ACL.SetAccessRuleProtection($true, $false)
$ACL.Access | ForEach-Object { $ACL.RemoveAccessRule($_) | Out-Null }
# Sadece gereken izinleri ekle
$SvcBackupRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"DOMAINsvc_backup", "Modify", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$AdminRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"BUILTINAdministrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$ACL.AddAccessRule($SvcBackupRule)
$ACL.AddAccessRule($AdminRule)
Set-Acl -Path $BackupPath -AclObject $ACL
Write-Host "Yedek klasoru izinleri ayarlandi."
BitLocker ile Yedek Diskini Şifreleme
Fiziksel güvenlik her zaman garanti değildir. Yedek disklerini BitLocker ile şifreleyin. Özellikle tape veya taşınabilir disk kullanıyorsanız bu zorunludur.
# Harici yedek diskini BitLocker ile şifrele
# D: sürücüsü yedek diski olduğunu varsayıyoruz
# BitLocker durumunu kontrol et
Get-BitLockerVolume -MountPoint "D:"
# BitLocker'ı etkinleştir, recovery key'i kaydet
Enable-BitLocker -MountPoint "D:" `
-EncryptionMethod XtsAes256 `
-RecoveryPasswordProtector
# Recovery key'i Active Directory'ye yedekle (önemli!)
$BLV = Get-BitLockerVolume -MountPoint "D:"
$RecoveryProtector = $BLV.KeyProtector | Where-Object { $_.KeyProtectorType -eq "RecoveryPassword" }
Backup-BitLockerKeyProtector -MountPoint "D:" -KeyProtectorId $RecoveryProtector.KeyProtectorId
# Şifreleme durumunu izle
Get-BitLockerVolume -MountPoint "D:" | Select-Object MountPoint, EncryptionPercentage, VolumeStatus
Veeam ile Kurumsal Yedekleme
Orta ve büyük ortamlarda Windows Server Backup yetmez. Veeam, bu alanda fiilen sektör standardı haline gelmiş durumdadır. Ücretsiz Community Edition ile 10 workload’a kadar lisanssız kullanabilirsiniz.
Veeam’ın komut satırı aracı olan veeam veya PowerShell modülüyle yönetim yapabilirsiniz.
# Veeam PowerShell modülünü yükle
Add-PSSnapin VeeamPSSnapIn -ErrorAction SilentlyContinue
# Tüm backup job'larını listele
Get-VBRJob | Select-Object Name, JobType, LastState, LastEndTime
# Belirli bir job'u manuel başlat
$Job = Get-VBRJob -Name "Daily-FileServer-Backup"
Start-VBRJob -Job $Job
# Son 7 günün yedekleme sonuçlarını raporla
Get-VBRBackupSession | Where-Object {
$_.CreationTime -gt (Get-Date).AddDays(-7)
} | Select-Object JobName, CreationTime, Result, Progress |
Format-Table -AutoSize
# Restore point'leri listele
Get-VBRRestorePoint | Where-Object { $_.Name -like "*FileServer*" } |
Select-Object Name, CreationTime, IsConsistent |
Sort-Object CreationTime -Descending |
Select-Object -First 10
Offsite Yedekleme: Azure Blob Storage Entegrasyonu
3-2-1 kuralının “1 kopya offsite” kısmı için Azure Blob Storage mükemmel bir seçenek. Hem ucuz hem güvenilir hem de PowerShell ile kolayca entegre edilebilir.
# Azure PowerShell modülü kurulu olmalı
# Install-Module Az -Scope CurrentUser
# Azure'a bağlan
Connect-AzAccount -TenantId "your-tenant-id"
# Storage account ve container bilgileri
$ResourceGroup = "rg-backup-prod"
$StorageAccount = "stbackupprod2024"
$Container = "server-backups"
$LocalBackupPath = "C:BackupsLatest"
# Storage context oluştur
$StorageContext = New-AzStorageContext `
-StorageAccountName $StorageAccount `
-StorageAccountKey (Get-AzStorageAccountKey -ResourceGroupName $ResourceGroup `
-Name $StorageAccount).Value[0]
# Yedek dosyalarını Azure'a yükle
$UploadDate = Get-Date -Format "yyyy/MM/dd"
$Files = Get-ChildItem -Path $LocalBackupPath -File -Recurse
foreach ($File in $Files) {
$BlobName = "$UploadDate/$(hostname)/$($File.Name)"
Write-Host "Yukleniyor: $($File.Name) -> $BlobName"
Set-AzStorageBlobContent `
-Context $StorageContext `
-Container $Container `
-File $File.FullName `
-Blob $BlobName `
-Force | Out-Null
Write-Host "Tamamlandi: $BlobName"
}
# Yüklenen blob'ları listele ve boyutunu raporla
$TotalSize = (Get-AzStorageBlob -Container $Container -Context $StorageContext |
Where-Object { $_.Name -like "$UploadDate/$(hostname)/*" } |
Measure-Object -Property Length -Sum).Sum
Write-Host "Toplam yuklenen: $('{0:F2}' -f ($TotalSize/1GB)) GB"
Yedekleme Doğrulama ve Test Prosedürü
Yedek aldım diyip geçmek yetmez. Hiç geri yükleme testi yapmamış bir yedek, belki de bozuk bir yedektir. Gerçek bir felaket anında bunu öğrenmek istemezsiniz.
Yedek doğrulama için aylık test prosedürü oluşturun ve bunu bir takvime bağlayın. Test ortamında geri yükleme yapın, kritik dosyaların gerçekten açılabildiğini kontrol edin.
# Yedek bütünlüğünü doğrulama betiği
# wbadmin ile alınan yedekler için
param(
[string]$BackupTarget = "E:",
[string]$ReportPath = "C:ReportsBackupVerification"
)
if (-not (Test-Path $ReportPath)) {
New-Item -ItemType Directory -Path $ReportPath -Force | Out-Null
}
$ReportFile = "$ReportPathverify_$(Get-Date -Format 'yyyyMMdd').txt"
$Results = @()
Write-Host "Yedek versiyonlari kontrol ediliyor..."
# Mevcut yedek versiyonlarını al
$Versions = wbadmin get versions -backupTarget:$BackupTarget 2>&1
$VersionLines = $Versions | Where-Object { $_ -match "Backup time:" }
if ($VersionLines.Count -eq 0) {
$Results += "UYARI: Hic yedek versiyonu bulunamadi!"
} else {
$Results += "Toplam yedek versiyonu: $($VersionLines.Count)"
# En son yedeğin tarihi
$LastBackupLine = $VersionLines | Select-Object -Last 1
$Results += "Son yedek: $LastBackupLine"
# Son yedek 24 saatten eskiyse uyar
if ($LastBackupLine -match "(d{1,2}/d{1,2}/d{4} d{1,2}:d{2} [AP]M)") {
$LastBackupTime = [DateTime]::Parse($Matches[1])
$HoursSinceBackup = ((Get-Date) - $LastBackupTime).TotalHours
if ($HoursSinceBackup -gt 24) {
$Results += "KRITIK: Son yedek $($HoursSinceBackup.ToString('F0')) saat once alindi!"
} else {
$Results += "OK: Son yedek $($HoursSinceBackup.ToString('F1')) saat once alindi."
}
}
}
# Yedek hedef disk doluluk oranı
$BackupDisk = Get-PSDrive -Name ($BackupTarget.TrimEnd(':'))
if ($BackupDisk) {
$UsedPercent = [math]::Round(($BackupDisk.Used / ($BackupDisk.Used + $BackupDisk.Free)) * 100, 1)
$Results += "Yedek diski doluluk: %$UsedPercent"
if ($UsedPercent -gt 85) {
$Results += "UYARI: Yedek diski %85 uzerinde dolu!"
}
}
# Raporu kaydet
$Results | ForEach-Object { Write-Host $_ }
$Results | Out-File -FilePath $ReportFile -Encoding UTF8
Write-Host "`nRapor kaydedildi: $ReportFile"
Gerçek Dünya Senaryosu: Fidye Yazılımına Karşı Yedekleme
2023’te karşılaştığım bir senaryoyu paylaşayım. Bir müşterinin file server’ı ransomware’e yakalandı. Şifreli dosyalar hızla backup share’e yayılmaya başladı çünkü backup servisi de aynı hesapla çalışıyordu ve share mount edilmişti. Sonuç: Hem production hem yedek şifreli.
Bu senaryoyu engellemek için alınması gereken önlemler:
- Yedek sunucusu production ağından izole edilmeli
- Yedekleme işlemi tamamlanınca share bağlantısı kesilmeli
- Immutable backup kullanılmalı, yani alınan yedek değiştirilemesin
- Azure Blob Storage’da “immutable storage policy” aktif edilmeli
- En az bir offline yedek tutulmalı
Azure’da immutable backup politikası oluşturmak için:
# Azure Blob Storage immutable policy ayarla
# Bu sayede yedekler belirli süre silinip değiştirilemez
$ResourceGroup = "rg-backup-prod"
$StorageAccount = "stbackupprod2024"
$Container = "immutable-backups"
# Container oluştur
New-AzStorageContainer -Name $Container -Context $StorageContext
# Immutable policy ekle (30 gün boyunca silinemez)
Add-AzRmStorageContainerImmutabilityPolicy `
-ResourceGroupName $ResourceGroup `
-StorageAccountName $StorageAccount `
-ContainerName $Container `
-ImmutabilityPeriod 30
# Policy'yi kilitle (locked yapınca admin bile silemez)
# DİKKAT: Bu işlem geri alınamaz!
# $Policy = Get-AzRmStorageContainerImmutabilityPolicy ...
# Lock-AzRmStorageContainerImmutabilityPolicy ...
Write-Host "Immutable policy 30 gun olarak ayarlandi."
Write-Host "Bu container'daki yedekler 30 gun boyunca silinemez veya degistirilemez."
Yedekleme İzleme ve Raporlama
Büyük ortamlarda her sunucunun yedeğini tek tek kontrol etmek mümkün değil. Merkezi bir izleme yapısı kurmanız gerekiyor.
Windows Event Log, yedekleme sonuçlarını kaydeder. Event ID 4 başarılı yedeklemeyi, Event ID 5 başarısız yedeklemeyi gösterir. Bu event’ları merkezi olarak toplayın, SIEM veya basit bir PowerShell betiğiyle günlük özet e-posta gönderin.
Küçük ortamlar için basit ama etkili yöntem: Her sunucuya yukarıda paylaştığım betikleri kurun, sonuçları merkezi bir log klasörüne yazsın, sabah ilk işiniz bu logları okumak olsun.
Orta ve büyük ortamlar için Veeam ONE, Backup Exec veya en azından bir SIEM entegrasyonu şart. Yedekleme olaylarını e-posta, Teams veya Slack kanalına yönlendirin, böylece ekipteki herkes durumu anlık takip edebilir.
Sonuç
Windows Server yedekleme stratejisi kurmak, bir kere yapılıp unutulan bir iş değildir. Ortamınız değiştikçe yedekleme stratejiniz de değişmeli. Yeni bir sunucu kurduğunuzda “yedekleme nasıl alınacak” sorusu checklist’inizin ilk maddesi olmalı.
En kritik hatırlatmalar şunlar:
- Test etmediğiniz yedek, yedek değildir. Ayda bir geri yükleme testi yapın.
- 3-2-1 kuralını uygulayın. Offsite yedek olmadan risk azaltmazsınız.
- Yedekleri şifreleyin. Özellikle taşınabilir medya ve bulut depolamada.
- Fidye yazılımına karşı immutable backup kullanın. Bu artık zorunluluktur.
- Yedekleme servis hesabını minimum yetkiyle çalıştırın. Ayrıcalıklı hesap kullanmaktan kaçının.
- Merkezi izleme kurun. Sabah gelen “yedek başarısız” e-postası, production’da veri kaybetmekten çok daha iyidir.
Felaket kurtarma planınızı bir Word dosyasına yazın, yedekleme prosedürlerinizi belgeleyin ve bu belgeyi ekiple paylaşın. Çünkü siz izindeyken de sistemler çalışmaya devam eder ve birisinin o an ne yapacağını bilmesi gerekir.