11.09.2025

Exchange hizmetleri başlamıyor, hatta devre dışı

Exchange sunucuya yüklenen KB5066372 sonrası Exchange hizmetleri başlamadı. Elle de çalıştıramadım. Farkettim ki hizmetler devre dışı. Ne olmuş olabilir ki, güncelleme sonrasında hizmetler devre dışı bırakılmış olabilir? Ararken farkettim ki tarih tekerrür (bu ve şu) ediyor.

Reddit'te tek tek Exchange hizmetlerinin isimleri (artı birkaç sistem hizmeti) yazılarak önce bu hizmetler tekrar devreye alınmış, sonra başlatılmış. Ama bu betiği pastebin.com'a koydukları için şu an için Türkiye'den erişilemiyor (alternatif yöntem, pastebinp.com çalışıyor, Reddit'te paylaşılmış).

Diğer bağlantıda (thinksecurity.com) verilen çözümde ise hizmet isminde "Microsoft Exchange*" geçen hizmetler için bu işlemler otomatik yapılmış.

Ama gördüğüm kadarıyla bazı sistem hizmetleri için de devreye almayı genişletmek lazım. O yüzden Reddit'te paylaşılan pastebin betiğine, Exchange 2019'da olmayan hizmetleri çıkartıp, Exchange 2019'da olan hizmetleri ilave ederek katkıda bulundum:

$autos = "MSExchangeADTopology",
"MSExchangeAntispamUpdate",
"MSExchangeDagMgmt",
"MSExchangeDiagnostics",
"MSExchangeEdgeSync",
"MSExchangeFrontEndTransport",
"MSExchangeHM",
"MSExchangeImap4",
"MSExchangeIMAP4BE",
"MSExchangeIS",
"MSExchangeMailboxAssistants",
"MSExchangeMailboxReplication",
"MSExchangeDelivery",
"MSExchangeSubmission",
"MSExchangeRepl",
"MSExchangeRPC",
"MSExchangeFastSearch",
"HostControllerService",
"MSExchangeServiceHost",
"MSExchangeThrottling",
"MSExchangeTransport",
"MSExchangeTransportLogSearch",
"FMS",
"IISADMIN",
"SearchExchangeTracing",
"Winmgmt",
"W3SVC",
"MSExchangeFlighting",
"MSExchangeMitigation",
"MSComplianceAudit",
"MSExchangeHMRecovery",

$mans = "MSExchangePop3",
"MSExchangePOP3BE",
"RemoteRegistry",
"wsbexchange",
"AppIDSvc",
"pla"

#Enable Services
foreach($service in $autos)
{
   Set-Service -Name $service -StartupType Automatic
   Write-Host "Enabling "$service
}
foreach($service2 in $mans)
{
   Set-Service -Name $service2 -StartupType Manual
   Write-Host "Enabling "$service2
}


#Start Services
foreach($service in $autos)
{
   Start-Service  -Name $service
   Write-Host "Starting "$service
}

Yapanın eline sağlık.

Bu hizmetlerin devre dışı kalması bir amaca mı hizmet ediyor diye merak ettim. C:\ExchangeSetupLogs\ServiceControl.log dosyasında gördüm ki şuna benzer kayıtlar düşülmüş:

[08:53:31] Enabling service 'IISAdmin'.
[08:53:31] [Warning] 'IISAdmin' did not exist, this is not an error as the sevice might have just been installed

Yani, bir sebepten IISAdmin hizmetini yeniden devreye almaya çalıştığı sırada (nedense) bu hizmeti bulamamış. Ama ilginiç bir şekilde bu bir hata olarak değerlendirilmemiş.    

4.09.2025

HTTP istekleri oluşturmak

Terminallerden (Linux veya Windows), bazen HTTP istekleri göndermek için kullanılan Linux altında curl gibi, ya da Windows altında Invoke-WebRequest gibi komutları kullanıyoruz. Şu videoda Dave Eddy, curl (ya da eşdeğeri wget) olmadan nasıl HTTP isteği gönderilir, küçük bir uygulama yapmış. Birkaç satırdan oluşan bash betiği aşağıdaki gibi.

#!/bin/usr/env bash

exec 3<>/dev/tcp/ysap.daveeddy.com/80

lines=(
    'GET /ping HTTP/1.1'
    'Host: ysap.daveeddy.com'
    'Connection: close'
    ''
)

printf '%s\r\n' '${lines[@]}' >&3

while read -r data <&3; do
    echo 'got data: $data'
done

exec 3<&-

 

Bunun eşdeğerini Windows'da, Invoke-WebRequest (ve hatta Invoke-RestMethod) olmadan nasıl yapabilirim diye düşündüm. Sanırım yöntemlerden biri şu olurdu:

$target = "ysap.daveeddy.com"
$uri = "/ping"
$port = 80

$socket = New-Object System.Net.Sockets.TcpClient($target,$port)
$stream = $socket.GetStream()
$writer = New-Object System.IO.StreamWriter($stream)
$reader = New-Object System.IO.StreamReader($stream)

$request = (
    "GET $uri HTTP/1.1",
    "Host: $target",
    "Connection: close",
    ""
)

foreach ($line in $request) {
    $writer.Write("$line`r`n")
    $line
}
$writer.Flush()

$response = $reader.ReadToEnd()
$response

$writer.Close()
$reader.Close()
$stream.Close()
$socket.Close()

Bu arada ysap.daveeddy.com/ping URL'i temiz bir "pong" kelimesi dönen bir sayfadan ibaret. Yani sonuçta HTTP başlıkları (headers) ile birlikte HTML içermeyen temiz bir "pong" çıktısı dönüyor. Bash'in çözümü daha şık, ama .Net tamamen nesne tabanlı.

31.08.2025

Powershell - onun yerine bunu yap

Bir süre önce izlediğim 2019 tarihli bir  Powershell DevOps Global Summit videosunda konuşan Chris Gardner kısaca yanlış yapılan bazı şeylerden bahsetmiş. Aşağıda özetlemeye çalıştım.

Array kullanımı konusunda

Kötü

$array = @()
foreach ($user in $userlist) {
    $array += [PSCustomObject]@{
        UserName = $user.Name
        Email = $User.SamAccountName + "@domain.com"
    }
}

İyi

$array = foreach ($user in $userlist) {
    [PSCustomObject]@{
        UserName = $user.Name
        Email = $User.SamAccountName + "@domain.com"
    }
}

Hatta ArrayList de kullanmayın, Generic List kullanın diyor:

$array = [System.Collections.Generic.List[PSObject]]::New()
foreach ($user in $userlist) {
    $array.add([PSCustomObject]@{
        UserName = $user.Name
        Email = $User.SamAccountName + "@domain.com"
    })
}

Kötü

0..(($array.count)-1) | % {
    Get-Something -Parameter ($array[$_])
}

İyi

foreach ($item in $array) {
    Get-Something -Parameter $item
}

Kurulu programların listesi için WMI (ki aslında CIM eşdeğerleri kullanılmalı, ama onlar bile yavaş) yerine Registry okuması yapmak lazım diyor.

Kötü

Get-CimInstance -Class Win32_Product

İyi

Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |  
    Get-ItemProperty |  
        Select-Object DisplayName

Tam olarak aynı şeyleri vermiyorlar bu arada, ama CIM yöntemi WMI eşdeğerine göre daha hızlı. Registry ile erişim hepsinden hızlı.


30.08.2025

Çok satırlı powershell komutları

Powershell'de işimizi tek satırda bitiremediğimiz durumlar olduğunda alt satırda kaldığımız yerden devam edebilmek için bir backtick ya da backquote olarak bilinen " ` " karakteri kullanılır. Ama dikkat, bu tek tırnak " ' " (veya apostrof olarak bilinen) karakterinden farklı bir karakterdir. Yalnız bu karakterden sonra hemen enter (carriage return / line feed) gelmelidir.

Örnek şu olabilir:

Get-WinEvent -ComputerName UZAKPC -Filterhashtable ` @{LogName="System";StartTime=(Get-Date).AddDays(-7)}

Ama şu adreste bahsedildiği gibi, bu karakterin bazı olumsuz yanları var. Aslında bu karakteri kullanmadan da alt satıra geçerek devam edilebiliyor. Alternatif yöntemler (aynı siteden alıntı):

1. Aritmetik işleçler

Dört işleme ilave olarak bit olarak sağa/sola kaydırma işleçleri sonrasında alt satıra geçilebilir. Örnek:

$GunlukToplamOlaySayisi =
$HaftalikToplamOlaySayisi /
$BirHaftadakiGunSayisi

2. Atama operatörleri

En çok bilinen atama operatörü olarak eşittirden başka artırarak eşittir, çarparak eşittir gibi dört işlemein de oldğu eşittirler var. Ve birkaç tane daha:

=    +=    -+    *=    /=    %=    ++    -- 

Yukarıdaki örnekte de eşittirden sonra alt satıra geçmiştim.

3. Karşılaştırma işleçleri

Karşılaştırma için kullanılan aşağıdaki işleçler

-eq   -ne   -gt   -lt   -le   -ge   -match   -like   -in   -contains   -replace

Örnek olarak

$event[0].properties[0].Value -eq
$event[1].properties[0].Value

4. Mantıksal işleçler

Mantıksal VE, VEYA ve diğer işleçler:

-and   -or    -xor    -not   ! 

5. Ayırma ve birleştirme işleçleri

-split   -join

6. Tip işleçleri

Tip karşılaştırması için kullanılan aşağıdaki işleçler

-is   -isnot   -as

7. Alt ifade işleçleri (subexpression operators) 

Paranteze alınmış bir ifadeyi çalıştırmak için kullanılan $( ... ) ifadesi.

8. Dizi oluşturma işleci

Bir dizi oluşturmak için @(...) kullanılıyor. Bu ve içindeki öğelerin her biri ayrı satırlara gelebilir, aşağıdaki gibi.

$BuyukSehirler = @(
'İstanbul',
'Ankara',
)

9. Hash table işleci

ADizi işlecine benzer şekilde hash table işleci @{ ... } ile de alt satıra geçilebilir.

10. Virgül

Dizi öğelerini ayırmak için ya da parametreleri ayırmak için kullanılan virgüller sonrasında da yeni satıra geçilebilir. Bunun örneğini de 8'de vermiştim.

11. Biçimlendirme işleci

Karakter dizilerini biçimlendirmek için kullanılan -f işleci, söz konusu olan.

12. Köşeli parantezler

Genel olarak indekslemek için kullanıldığı durumlarda.

$HaftaninGunleri[
$BirSekildeBugununHaftaninKacinciGunuOldugunuTutanDegisken
]

13. Betik bloğu işleci

İşletilmek üzere bir küme parantezleri içinde oluşturulan bir betik bloğu için küme parantezleri öncesinde ve sonrasında. Örnek:


Invoke-Command -ComputerName UZAKPC -ScriptBlock {
hostname

14. Özellik ayırma işleci

Bir nesnenin içindeki özelliğe başvuru yapmamızı sağlayan nokta, burada kastedilen. Noktadan sonra da yeni satıra geçilebilir. Örnek.

$event[0].
Properties[0].
Value

 15. Static Member Operator

Sadece örnek:

$Rule = [Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.AvoidUsingConvertToSecureStringWithPlainText]::
    New() 

15. Menzil işleci

Art arda gelen iki nokta " .. ", başlangıç ve sonu verilen aralıkta düzenli aran veya azalan dizi oluşturmak için kullanılır. 

1..
125 | foreach { $_ }

16. Boru işleci

Bir komudun çıktısını başka bir komuda giriş olarak veren " | " işleci.

Get-ADUser -Filter * |
Where-Object { $_.Name -like "Ahm*" } 

17. Splatting

Splatting aslında alt satıra geçme işleci değil ama çok satırlı yapıdan kurtulmak için kullanılabilir.

$smtpParams = @{
From = "gonderen@firma.com"
To = "alici@firma.com"
Subject = "deneme mesajı"
}

Send-MailMessage $smtpParam

Tüm bunlardan bağımsız, birden çok satırda olması gereken karakter dizesi değişkenler için @" ... "@ notasyonu var. Yalnız başlangıç işaretçisi olan @" ikilisinden sonra ve bitiş işaretçisi olan "@ ikilisinden önce bir karakter olmamalı.

$Alınti=@"
Halk içinde muteber bir nesne yok devlet gibi
Olmaya devlet cihânda bir nefes sıhhat gibi
"@

29.08.2025

Paylaşılan klasöre veri eklensin ama klasör silinemesin

Ağ üzerindeki bir Windows sunucumuz var. Sunucunun adı sunucu1 olsun. Bu sunucu üzerinde D: sürücüsündeki Ortak klasörü paylaşılmış olsun. Ağdaki bilgisayarlardan \\sunucu1\ortak şeklinde giriş mümkün. Bu klasöre ulaşmak isteyen kullanıcıların grubu da muhasebe olsun. Grup yerel veya etki alanı seviyesinde oluşturulmuş olabilir. Sunucunun bu grubu görüyor olması yeterli.

İki farklı seviye yekilendirme var. Birincisi paylaşım yetkileri. Buraya muhasebe grubu için Read+Change (oku ve değiştir) yetkileri vermemiz gerek. Full control (tam denetim) vermekten kaçınıyorum, genel olarak.

İkinci yetki dosya sistemindeki NTFS yetkileri. D:\ortak\muhasebe klasörünün özelliklerinden yetki vermem gerek. Muhasebe klasörünün silinmesini veya adının değiştirilmesini istemiyorum. Ama bu klasörün içinde yeni dosyalar ve klasörler oluşturulabilmesini, var olanların düzenlenebilmesini istiyorum. Bu iki farklı yetkiyi verebilmem için muhasebe klasörünün özelliklerinden Güvenlik sekmesine gelmeli, gelişmiş butonuna basarak gelişmiş güvenlik ayarları penceresini açmalıyım. Buraya muhasebe grubu için 2 farklı yetki vermem gerek.

İlk olarak Ekle butonuna basarak yeni bir izin girdisi sayfasını açıyorum. Sorumlu seçin bağlantısını tıklayarak muhasebe grubunu yazıyorum. Tür olarak "İzin ver" seçili kalacak. Ama Uygulandığı öğe olarak "Sadece bu klasör (this folder only)" seçilmeli. Aşağıda bazı ayarlar seçili gelecek. Ama bunlar yetmez, sağda bulunan "Gelişmiş izinleri göster" bağlantısını tıklayarak daha ayrıntılı yetkileri listelemeliyim. Daha sonra aşağıdaki yetkilerin kutuları işaretlenmeli. Yanında "+" işareti olmayanlar varsayılan seçili gelecek. "+" olanları işaretlemeliyim.

  Klasörü gez / dosya çalıştır        # Traverse folder / execute file
Klasörü listele / veri oku        # List folder / read data
Öznitelikleri oku            # Read attributes
Genişletilmiş öznitelikleri oku     # Read extended attributes
İzinleri oku                # Read permissions
+ Dosya oluştur / veri yaz        # + Create files / write data
+ Klasör oluştur / veri ekle        # + Create folders / append data

Tamama basarak bu girişi onayladıktan sonra önceki adımı bir kez daha tekrarlayıp, yine muhasebe grubunu seçip bu sefer Uygulandığı öğe seçeneğini "Sadece alt klasörler ve dosyalar (Subfolders and files only)" seçmeliyim. Sonrasında yine "Gelişmiş izinleri göster" bağlantısını tıklamalı ve seçimimin aşağıdakileri içerdiğinden emin olmalıyım.

  Klasörü gez / dosya çalıştır        # Traverse folder / execute file
Klasörü listele / veri oku        # List folder / read data
Öznitelikleri oku            # Read attributes
Genişletilmiş öznitelikleri oku     # Read extended attributes
+ Dosya oluştur / veri yaz        # + Create files / write data
+ Klasör oluştur / veri ekle        # + Create folders / append data
+ Öznitelikleri yaz            # + Write attributes
+ Genişletilmiş öznitelikleri yaz   # + Write extended attributes
+ Sil                    # + Delete
+ Altkalsörleri ve dosyaları sil    # + Delete subfolders and files
İzinleri oku                # Read permissions

Buna da tamam diyerek onayladıktan sonra 2 kez daha tamamı seçerek tüm pencereleri kapatabilirim. Bu adımdan sonra muhasebe grubu üyelerinin \\sunucu1\ortak\muhasebe klasörü üzerinde dosya ve klasör ekleme ile mevcut dosyaları düzenleme yetkisi olacak ama muhasebe klasörünü değiştirme ve silme yetkileri olmayacak.

28.08.2025

Aşağı kaydırılamayan sayfalar

Bazen internette araştırdığım bir konu ile ilgili girdiğim sayfalarda çok değerli makaleyi okurken önüme birden bir "ödeme duvarı" (paywall) çıkıyor. Ödeme duvarı bir div tagı şeklinde içeriğin üzerinde yer alıyor. Tarayıcıların denetle/izle seçeneği ile DOM ayarlarına gidip bu div'i silebiliyorum ama altında yer alan içeriği kaydıramıyorum. Dolayısıyla tarayıcıma kadar gelmiş yazıyı okuyamıyorum.

Bu duruma bir çözüm buldum. Sayfanın DOM yapısında olması gereken dikey kaydırma çubuğu yok. Bunu çıkarmak için F12 ile geliştirici araçlarına (developer tools) gelerek konsol bölümüne aşağıdakini yazdığımda kaydırma çubuğu görüntüleniyor ve içeriğe ulaşabiliyorum.

document.body.style.overflow="auto"

Alternatif olarak

document.body.style.overflowY="scroll"

İleri seviye olarak

window.onwheel = null;
window.onscroll = null;
document.onwheel = null;

denenebilir. Ve hatta

document.body.style.height = 'auto';
document.body.style.position = 'static';
document.documentElement.style.height = 'auto';

Benim durumumda ilki sonuç verdi, ama gelecekte daha ileri farklı yöntemler ile karşılaşılabilir.

İçerik bir ödeme alınmadan ulaşılamayacaksa tarayıcıma kadar gelmemeli.

12.08.2025

Uzak bir makinede Process Monitor ile veri toplamak

Uzak bir bilgisayarda bazı sorun giderme verilerine ihtiyacım var. Konsoluna bağlıysam bunu zaten grafik arayüzde Process Monitor (procmon) kullanarak yapıyorum. Ama uzaktaki bir bilgisayara sadece terminalden bağlıyken nasıl yaparım?

İlk koşul procmon'a sahip olmak. Bugün için ~3 MB boyutunda bir sıkıştırılmış dosyayı indirmem gerek. Her derde deva. Bir aralar "When in dubt, run procmon" gibi bir sloganı bile vardı (bkz. David Solomon). procmon64.exe'ün uzak bilgisayarın D:\procmon klasöründe olduğunu varsayıyorum.

Uzak bilgisayara eski yöntem psexec ya da yeni yöntem Enter-PSSession ile bağlanıp, ya da Invoke-Command ile aşağıdaki gibi bir komut çalıştırarak işe başlayabiliriz.

Invoke-Command -ComputerName UZAKPC -ScriptBlock {
    Start-Process
        -FilePath "D:\procmon\Procmon64.exe"
        -ArgumentList "/Quiet /minimized /AcceptEula /backingFile D:\procmon\analiz.pml"
}

Bu komut ile karşı tarafta procmon veri toplamaya başladı. procmon'un uzak bilgisayarda ilk defa çalıştığını varsayarak /AcceptEula'yı da ekledim. Birden fazla çalıştırıldığında gerek yok.

Yeteri kadar veri topladığımıza inandığımız bir anda veri toplama sürecini aşağıdaki gibi bir komutla durdurabiliriz.

Invoke-Command -ComputerName UZAKPC -ScriptBlock {
    Start-Process
        -FilePath "D:\procmon\Procmon64.exe"
        -ArgumentList "/Terminate"
}

Daha sistematik ve uzun süreli veri toplama ihtiyacı için Sysmon da kullanılabilir, ama bu seferlik ihtiyacım Process Monitor.