PowerShell ile Exchange Server Yönetimi
Exchange ortamında PowerShell kullanmaya başladığım ilk günü hâlâ hatırlıyorum. GUI üzerinden saatler harcayarak yaptığım bir posta kutusu taşıma işlemini, sonradan tek satır komutla hallettim. O andan itibaren EMS (Exchange Management Shell) benim için bir araç olmaktan çıkıp bir alışkanlığa dönüştü. Eğer siz de Exchange yönetimini hâlâ grafik arayüzden yapıyorsanız, bu yazının sizi biraz rahatsız edeceğini söyleyeyim, ama bu iyi bir rahatsızlık.
Exchange Management Shell’e Giriş
Exchange Server, kurulumdan itibaren PowerShell modüllerini sisteme entegre eder. EMS, aslında Exchange snap-in’lerinin yüklendiği özelleştirilmiş bir PowerShell ortamıdır. Exchange 2016 ve 2019 sürümlerinde doğrudan PowerShell üzerinden de bağlanabilirsiniz, ancak EMS’i kullanmak birçok durumda daha pratiktir.
Uzak bir Exchange sunucusuna bağlanmak için şu yöntemi kullanabilirsiniz:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri http://EXCHANGE-SERVER/PowerShell/ `
-Authentication Kerberos
Import-PSSession $Session -DisableNameChecking
Bu komut bloğunu bir profil dosyasına koyarsanız, her oturum açışınızda otomatik olarak Exchange ortamınıza bağlanırsınız. Özellikle birden fazla Exchange organizasyonunu yöneten kişiler için bu küçük otomasyon bile ciddi zaman kazandırır.
Posta Kutusu Yönetimi: Temel Operasyonlar
Exchange yönetiminin kalbinde posta kutusu operasyonları yatar. Yeni bir kullanıcı oluşturmak, kota ayarlamak, devre dışı bırakmak… Bunların hepsini komut satırından yapmak hem hız hem de tutarlılık açısından GUI’ye kıyasla çok daha avantajlıdır.
Yeni bir posta kutusu oluşturmak için:
New-Mailbox -Name "Ahmet Yilmaz" `
-UserPrincipalName "[email protected]" `
-OrganizationalUnit "OU=Kullanicilar,DC=sirket,DC=com,DC=tr" `
-Password (ConvertTo-SecureString "Parola123!" -AsPlainText -Force) `
-ResetPasswordOnNextLogon $true `
-Database "Muhasebe-DB"
Toplu posta kutusu oluşturma senaryoları çok daha sık karşılaşılan bir durumdur. Örneğin yeni bir şube açıldığında 50 kullanıcı için posta kutusu açmanız gerektiğinde CSV ile çalışmak hayat kurtarır:
Import-Csv "C:kullanicilar.csv" | ForEach-Object {
New-Mailbox -Name $_.AdSoyad `
-UserPrincipalName $_.UPN `
-OrganizationalUnit $_.OU `
-Password (ConvertTo-SecureString $_.Parola -AsPlainText -Force) `
-Database $_.VeritabaniAdi `
-ResetPasswordOnNextLogon $true
Write-Host "$($_.AdSoyad) posta kutusu olusturuldu." -ForegroundColor Green
}
CSV dosyanızın sütun başlıkları komuttaki değişken adlarıyla birebir eşleşmeli. Bunu çok basit bir kural gibi görünse de gece acele ile yapılan operasyonlarda en sık karşılaşılan hatadır.
Kota Yönetimi ve Toplu Güncelleme
Posta kutusu kotalarını merkezi olarak yönetmek, özellikle storage planlaması açısından kritik önem taşır. Tek bir posta kutusunun kotasını güncellemek şöyle yapılır:
Set-Mailbox -Identity "[email protected]" `
-IssueWarningQuota 4GB `
-ProhibitSendQuota 4.5GB `
-ProhibitSendReceiveQuota 5GB `
-UseDatabaseQuotaDefaults $false
Fakat gerçek dünyada daha çok toplu güncelleme senaryolarıyla karşılaşırsınız. Diyelim ki finans departmanındaki tüm kullanıcıların kotasını artırmanız gerekiyor:
Get-Mailbox -OrganizationalUnit "OU=Finans,OU=Kullanicilar,DC=sirket,DC=com,DC=tr" |
Set-Mailbox `
-IssueWarningQuota 8GB `
-ProhibitSendQuota 9GB `
-ProhibitSendReceiveQuota 10GB `
-UseDatabaseQuotaDefaults $false
Pipeline’ın gücünü burada görüyorsunuz. Get-Mailbox çıktısını doğrudan Set-Mailbox‘a besleyebiliyorsunuz. Bu yaklaşımı öğrendiğinizde Exchange yönetimindeki verimliliğiniz katlıyor.
Dağıtım Grupları ve Güvenlik Grupları
Exchange ortamında grup yönetimi, posta kutusu yönetiminden çok daha karmaşık bir hal alabilir. Özellikle büyük organizasyonlarda kim hangi gruba üye, hangi grubun sahibi kim, dinamik dağıtım grupları nasıl çalışıyor gibi sorular günlük rutinin parçası haline gelir.
Yeni bir dağıtım grubu oluşturmak:
New-DistributionGroup -Name "Proje-Ekibi-Alpha" `
-Alias "proje-alpha" `
-PrimarySmtpAddress "[email protected]" `
-OrganizationalUnit "OU=Gruplar,DC=sirket,DC=com,DC=tr" `
-ManagedBy "[email protected]" `
-MemberJoinRestriction Closed
-MemberJoinRestriction Closed parametresi dışarıdan kimsenin gruba kendiliğinden katılamamasını sağlar. Güvenlik açısından kritik gruplarda bu ayarı her zaman kapalı tutmanızı öneririm.
Bir grubun tüm üyelerini listelemek ve bunu raporlamak için:
$GrupAdi = "Proje-Ekibi-Alpha"
Get-DistributionGroupMember -Identity $GrupAdi |
Select-Object DisplayName, PrimarySmtpAddress, RecipientType |
Export-Csv "C:Raporlar$GrupAdi-Uyeler.csv" -NoTypeInformation -Encoding UTF8
-Encoding UTF8 parametresini eklemeyi unutmayın. Türkçe karakter içeren ad soyad bilgilerinde bu parametre olmadan CSV dosyanız bozuk görünecektir.
Mesaj İzleme ve Sorun Giderme
Exchange yöneticilerinin en çok zaman harcadığı alanlardan biri, kullanıcıların “mailim gitmiyor” ya da “mail almıyorum” şikayetlerini araştırmaktır. Message tracking log’ları bu noktada birincil kaynağınız olacak.
Get-MessageTrackingLog `
-Server "EXCHANGE01" `
-Start (Get-Date).AddHours(-4) `
-End (Get-Date) `
-Sender "[email protected]" `
-EventId DELIVER |
Select-Object Timestamp, Sender, Recipients, MessageSubject, Source |
Format-Table -AutoSize
Bu komut son 4 saat içinde belirli bir göndericinin iletilerinin teslim durumunu gösterir. DELIVER yerine FAIL veya REDIRECT event ID’lerini kullanarak sorunlu iletileri filtreleyebilirsiniz.
Daha karmaşık bir senaryo düşünelim: Belirli bir dış adrese giden tüm maillerin takibini yapmak istiyorsunuz.
$BaslangicTarihi = (Get-Date).AddDays(-7)
$BitisTarihi = Get-Date
$HedefAdres = "*@musteri-firma.com"
Get-MessageTrackingLog `
-Start $BaslangicTarihi `
-End $BitisTarihi `
-ResultSize Unlimited |
Where-Object {
$_.Recipients -like $HedefAdres -and
$_.EventId -eq "SEND"
} |
Select-Object Timestamp, Sender, Recipients, MessageSubject |
Export-Csv "C:RaporlarMusteriFirma-Yazismalar.csv" -NoTypeInformation -Encoding UTF8
-ResultSize Unlimited parametresini eklemeyi alışkanlık haline getirin. Varsayılan değer 1000 satırla sınırlıdır ve büyük ortamlarda eksik veri almanıza neden olur.
Veritabanı Yönetimi ve İzleme
Exchange veritabanlarının sağlığını düzenli olarak izlemek, olası sorunları önceden tespit etmenin en iyi yoludur. Bu yüzden otomatik çalışan izleme scriptleri hazırlamak Exchange yöneticilerinin işini önemli ölçüde kolaylaştırır.
Get-MailboxDatabase -Status |
Select-Object Name, Server, DatabaseSize, AvailableNewMailboxSpace, Mounted |
ForEach-Object {
$BoyutGB = [math]::Round($_.DatabaseSize.ToBytes() / 1GB, 2)
$BosGB = [math]::Round($_.AvailableNewMailboxSpace.ToBytes() / 1GB, 2)
$DolulukYuzdesi = [math]::Round((($BoyutGB - $BosGB) / $BoyutGB) * 100, 1)
[PSCustomObject]@{
VeritabaniAdi = $_.Name
Sunucu = $_.Server
ToplamBoyutGB = $BoyutGB
BosAlanGB = $BosGB
DolulukYuzdesi = "$DolulukYuzdesi%"
Mounted = $_.Mounted
}
} |
Format-Table -AutoSize
Bu scripti bir scheduled task ile her sabah çalıştırıp sonuçları e-posta ile almak, günlük kapasiteli planlaması için sizi önce haber veren erken uyarı sistemi görevi görür.
Arşiv Posta Kutuları ve Bekletme Politikaları
Uyumluluk gereksinimleri olan organizasyonlarda arşiv posta kutularını ve bekletme politikalarını yönetmek önemli bir sorumluluk alanıdır. Özellikle finans, sağlık ve kamu sektöründeki kurumlar için bu konfigürasyonlar denetim süreçlerinin kritik bir parçasıdır.
Bir kullanıcı için arşiv posta kutusu etkinleştirmek:
Enable-Mailbox -Identity "[email protected]" `
-Archive `
-ArchiveDatabase "Arsiv-DB-2024"
Birden fazla kullanıcı için toplu arşiv etkinleştirmesi:
Get-Mailbox -Filter {ArchiveStatus -eq "None" -and RecipientTypeDetails -eq "UserMailbox"} |
Where-Object { $_.WhenCreated -lt (Get-Date).AddYears(-1) } |
Enable-Mailbox -Archive -ArchiveDatabase "Arsiv-DB-2024"
Bu komut, bir yıldan eski oluşturulmuş ve henüz arşivi bulunmayan tüm kullanıcı posta kutularına arşiv ekler. Sonuçta kaç posta kutusunun etkileneceğini önce Select-Object DisplayName, WhenCreated ekleyerek kontrol etmenizi öneririm.
İzin Yönetimi: Delegasyon ve Paylaşımlı Posta Kutuları
Sekreterlik senaryoları, yönetici asistanları veya departman paylaşımlı hesapları için izin yönetimi sıkça gelen talepler arasındadır.
Bir kullanıcıya başkasının posta kutusuna erişim vermek:
# Full Access izni
Add-MailboxPermission `
-Identity "[email protected]" `
-User "[email protected]" `
-AccessRights FullAccess `
-InheritanceType All `
-AutoMapping $true
# Send As izni
Add-ADPermission `
-Identity "[email protected]" `
-User "[email protected]" `
-ExtendedRights "Send As"
-AutoMapping $true parametresi, Outlook’un bu posta kutusunu asistanın profiline otomatik eklemesini sağlar. Bazı durumlarda bu davranış istenmiyor olabilir, özellikle çok sayıda paylaşımlı posta kutusu olan kullanıcılarda Outlook başlangıç süresi uzayabilir.
Tüm organizasyondaki posta kutusu izinlerini raporlamak:
Get-Mailbox -ResultSize Unlimited |
Get-MailboxPermission |
Where-Object {
$_.User -notlike "NT AUTHORITY*" -and
$_.User -notlike "S-1-5*" -and
$_.IsInherited -eq $false
} |
Select-Object Identity, User, AccessRights |
Export-Csv "C:RaporlarPostaKutusu-Izinler.csv" -NoTypeInformation -Encoding UTF8
Bu raporu periyodik olarak üretmek, özellikle güvenlik denetimleri öncesinde “kim kimin posta kutusuna erişebiliyor” sorusuna hızlı cevap vermenizi sağlar.
Otomatik Yanıtlar ve Bant Dışı Mesajlar
Toplu bant dışı mesaj yönetimi, özellikle tatil dönemlerinde veya organizasyon değişikliklerinde sıkça ihtiyaç duyulan bir operasyondur.
# Bir kullanıcının otomatik yanıt durumunu kontrol et
Get-MailboxAutoReplyConfiguration -Identity "[email protected]" |
Select-Object AutoReplyState, StartTime, EndTime, InternalMessage
# Otomatik yanıt ayarla
Set-MailboxAutoReplyConfiguration `
-Identity "[email protected]" `
-AutoReplyState Scheduled `
-StartTime "2024-01-15 09:00:00" `
-EndTime "2024-01-22 18:00:00" `
-InternalMessage "Yillik izindeyim. Acil durumlar icin yoneticiminize basvurunuz." `
-ExternalMessage "I am on annual leave until January 22nd."
Tüm aktif otomatik yanıtları listelemek de periyodik olarak yapılması gereken bir kontrol:
Get-Mailbox -ResultSize Unlimited |
Get-MailboxAutoReplyConfiguration |
Where-Object { $_.AutoReplyState -ne "Disabled" } |
Select-Object Identity, AutoReplyState, StartTime, EndTime |
Format-Table -AutoSize
Sağlık Kontrolleri için Script Altyapısı
Tüm bu komutları bir araya getiren ve düzenli çalışan bir sağlık kontrol scripti oluşturmak, Exchange yönetiminde reaktif değil proaktif bir yaklaşım benimsemenizi sağlar.
function Test-ExchangeSagligi {
param(
[string]$RaporDizini = "C:RaporlarExchange",
[string]$BildirimAdresi = "[email protected]"
)
$Tarih = Get-Date -Format "yyyyMMdd-HHmm"
$Uyarilar = @()
# Veritabani durumu kontrolu
$Veritabanlari = Get-MailboxDatabase -Status
foreach ($DB in $Veritabanlari) {
if (-not $DB.Mounted) {
$Uyarilar += "KRITIK: $($DB.Name) veritabani mounted degil!"
}
$BoyutGB = [math]::Round($DB.DatabaseSize.ToBytes() / 1GB, 2)
if ($BoyutGB -gt 500) {
$Uyarilar += "UYARI: $($DB.Name) boyutu ${BoyutGB}GB siniri asti."
}
}
# Kuyruk durumu kontrolu
$Kuyruklar = Get-Queue | Where-Object { $_.MessageCount -gt 100 }
foreach ($Kuyruk in $Kuyruklar) {
$Uyarilar += "UYARI: $($Kuyruk.Identity) kuyruğunda $($Kuyruk.MessageCount) mesaj birikti."
}
# Uyari varsa bildirim gonder
if ($Uyarilar.Count -gt 0) {
$GovdeMetni = $Uyarilar -join "`n"
Send-MailMessage `
-To $BildirimAdresi `
-From "[email protected]" `
-Subject "Exchange Saglik Uyarisi - $Tarih" `
-Body $GovdeMetni `
-SmtpServer "localhost"
}
return $Uyarilar
}
Test-ExchangeSagligi
Bu fonksiyonu bir scheduled task ile her 30 dakikada bir çalıştırabilirsiniz. Sorun olduğunda sizi bilgilendiren, olmadığında sessiz kalan bir yapı olması onu günlük operasyonun fark edilmeden arka planda çalışan değerli bir parçası yapar.
Sonuç
PowerShell ile Exchange yönetimi, başlangıçta fazladan çaba gerektiriyor gibi görünse de orta ve uzun vadede sizi hem hatalardan hem de zaman kaybından korur. GUI üzerinden yapılan bir işlem her seferinde aynı adımlarla tekrar edilmek zorundadır. Bir PowerShell scripti ise bir kez yazılır, defalarca çalıştırılır ve üzerine inşa edilir.
Burada ele aldığımız konular, Exchange yönetiminin yalnızca temel katmanlarını oluşturuyor. DAG (Database Availability Group) yönetimi, transport kuralları, Hybrid ortam konfigürasyonları ve uyumluluk arama işlemleri gibi daha ileri konular ayrı yazıların hakkını hak ediyor. Ama bu temel üzerinde sağlam durursanız, o ileri konulara geçiş çok daha kolay olacak.
Son bir pratik öneri: Üretim ortamında çalıştıracağınız her büyük komutu önce -WhatIf parametresiyle test edin. Exchange’deki Set-Mailbox, Remove-Mailbox gibi komutların çoğu bu parametreyi destekler ve size “bunu çalıştırsaydım ne olurdu” sorusunun cevabını gerçekten bir şeyi değiştirmeden gösterir. Gece 02:00’de acil bir operasyon yaparken bu küçük alışkanlık sizi büyük bir felaketten kurtarabilir.
