Selamlar, bu yazımda IIS Configuration Değişikliklerini Splunk ile İzlemeyi ele alacağız. Şimdiden keyifli okumalar. 🙂 Bu çalışmanın amacı, IIS sunucularındaki ana konfigürasyon dosyası olan applicationHost.config üzerinde oluşabilecek yetkili veya yetkisiz değişiklikleri en geç beş dakika içinde tespit etmektir. Çözüm yalnızca hash değişikliğini görmekle kalmaz; eklenen ve silinen satırları JSON event içerisine ekler, değişiklik anındaki dosyanın snapshot’ını alır ve olayı Splunk üzerinde alarm üretilebilecek hale getirir.
- Dosyanın SHA-256 hash değeri baseline ile karşılaştırılır.
- Yeni değişiklikte added_lines ve removed_lines alanları oluşturulur.
- Her yeni hash için zaman damgalı snapshot alınır.
- Aynı hash için tekrar CHANGED eventi üretilmez.
- UNCHANGED durumları Splunk’a gönderilmez.
- Aynı UF app’i standalone ve cluster yapılarda kullanılabilir.

2. Çözümün Çalışma Mantığı
applicationHost.config
↓
PowerShell script hash hesaplar
↓
Baseline hash ile karşılaştırır
↓
Aynı ise: çıktı üretmeden sonlanır
Farklı ise: duplicate state kontrolü yapılır
↓
Yeni hash ise:
– Snapshot alınır
– Eklenen/silinen satırlar çıkarılır
– JSON event stdout’a yazılır
↓
Splunk Universal Forwarder
↓
index=iis_config_integrity
↓
Alarm / inceleme / geri dönüş
Baseline, snapshot, log ve state dosyaları Splunk app dizininde değil, C:\ProgramData\IISConfigMonitor altında tutulur. Böylece Deployment Server app’i yeniden dağıttığında çalışma verileri korunur.
3. Windows IIS Sunucusunda Universal Forwarder App’i
Standalone POC’de app doğrudan Windows Universal Forwarder’ın etc\apps dizinine konulur. Üretim ortamında aynı app Deployment Server üzerindeki deployment-apps dizininden IIS sunucularına dağıtılır.
3.1 App klasörü nerede oluşturulacak?
Sunucu: Windows IIS sunucusu
Bileşen: Splunk Universal Forwarder
App adı: TA-iis-config-integrity
Tam yol: C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity
C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\
TA-iis-config-integrity
├── bin
│ ├── iis_config_integrity.ps1
│ └── run_iis_config_integrity.cmd
└── local
├── inputs.conf
└── app.conf
Klasörleri oluşturma komutları:
$UFHome = “C:\Program Files\SplunkUniversalForwarder”
$AppRoot = “$UFHome\etc\apps\TA-iis-config-integrity”
New-Item “$AppRoot\bin” -ItemType Directory -Force | Out-Null
New-Item “$AppRoot\local” -ItemType Directory -Force | Out-Null
3.2 PowerShell scripti nereye konulacak?
Dosya adı: iis_config_integrity.ps1
Tam yol: C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\bin\iis_config_integrity.ps1
C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\bin\iis_config_integrity.ps1
Aşağıdaki script bin dizinine iis_config_integrity.ps1 adıyla kaydedilir. İzlenen dosya, scriptin başındaki ConfigPath değişkenidir. applicationHost.config dışında farklı bir web.config izlenecekse bu değişken veya ileride merkezi path listesi genişletilebilir.
[CmdletBinding()]
param()
$ErrorActionPreference = “Stop”
# AYARLAR
$ConfigPath = “$env:windir\System32\inetsrv\config\applicationHost.config”
$MonitorRoot = “C:\ProgramData\IISConfigMonitor”
$BaselineDirectory = Join-Path $MonitorRoot “baseline”
$SnapshotDirectory = Join-Path $MonitorRoot “snapshots”
$LogDirectory = Join-Path $MonitorRoot “logs”
$StateDirectory = Join-Path $MonitorRoot “state”
$BaselinePath = Join-Path $BaselineDirectory “applicationHost.config”
$HashPath = Join-Path $BaselineDirectory “applicationHost.config.sha256”
$LogPath = Join-Path $LogDirectory “iis_config_integrity.json”
$LastAlertedHashPath = Join-Path $StateDirectory “last_alerted_hash.txt”
function Write-IntegrityLog {
param(
[Parameter(Mandatory)]
[hashtable]$Data,
[switch]$SendToSplunk
)
$Data.timestamp = (Get-Date).ToString(“o”)
$Data.host = $env:COMPUTERNAME
$Json = $Data | ConvertTo-Json -Compress -Depth 10
Add-Content -Path $LogPath -Value $Json -Encoding UTF8
if ($SendToSplunk) {
Write-Output $Json
}
}
function Get-CleanFileContent {
param(
[Parameter(Mandatory)]
[string]$Path
)
return @(
Get-Content -Path $Path -ErrorAction Stop |
ForEach-Object { [string]$_ }
)
}
# Runtime klasörleri
foreach ($Directory in @(
$BaselineDirectory,
$SnapshotDirectory,
$LogDirectory,
$StateDirectory
)) {
if (-not (Test-Path $Directory -PathType Container)) {
New-Item -Path $Directory -ItemType Directory -Force | Out-Null
}
}
# Config dosyası var mı?
if (-not (Test-Path $ConfigPath -PathType Leaf)) {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “FILE_NOT_FOUND”
severity = “critical”
file_path = $ConfigPath
message = “IIS configuration file could not be found.”
}
return
}
# XML geçerli mi?
try {
[xml]$CurrentXml = Get-Content -Path $ConfigPath -Raw -ErrorAction Stop
}
catch {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “INVALID_XML”
severity = “critical”
file_path = $ConfigPath
message = $_.Exception.Message
}
return
}
# Güncel hash
try {
$CurrentHash = (
Get-FileHash -Path $ConfigPath -Algorithm SHA256 -ErrorAction Stop
).Hash
}
catch {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “HASH_ERROR”
severity = “critical”
file_path = $ConfigPath
message = $_.Exception.Message
}
return
}
# İlk baseline
if (
-not (Test-Path $BaselinePath -PathType Leaf) -or
-not (Test-Path $HashPath -PathType Leaf)
) {
try {
Copy-Item -Path $ConfigPath -Destination $BaselinePath -Force
$CurrentHash | Set-Content -Path $HashPath -Encoding ASCII
}
catch {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “BASELINE_CREATE_ERROR”
severity = “critical”
file_path = $ConfigPath
message = $_.Exception.Message
}
return
}
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “BASELINE_CREATED”
severity = “informational”
file_path = $ConfigPath
hash_algorithm = “SHA256”
current_hash = $CurrentHash
}
return
}
# Baseline hash
try {
$BaselineHash = (
Get-Content -Path $HashPath -Raw -ErrorAction Stop
).Trim()
}
catch {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “BASELINE_READ_ERROR”
severity = “critical”
file_path = $ConfigPath
message = $_.Exception.Message
}
return
}
# Değişiklik yok
if ($CurrentHash -eq $BaselineHash) {
if (Test-Path $LastAlertedHashPath -PathType Leaf) {
Remove-Item $LastAlertedHashPath -Force -ErrorAction SilentlyContinue
}
return
}
# Duplicate kontrolü
if (Test-Path $LastAlertedHashPath -PathType Leaf) {
$LastAlertedHash = (
Get-Content -Path $LastAlertedHashPath -Raw `
-ErrorAction SilentlyContinue
).Trim()
if ($CurrentHash -eq $LastAlertedHash) {
return
}
}
# Snapshot
$DetectionTime = Get-Date -Format “yyyyMMdd_HHmmss_fff”
$SnapshotPath = Join-Path `
$SnapshotDirectory `
“applicationHost_$DetectionTime.config”
try {
Copy-Item -Path $ConfigPath -Destination $SnapshotPath -Force
}
catch {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “SNAPSHOT_ERROR”
severity = “critical”
file_path = $ConfigPath
baseline_hash = $BaselineHash
current_hash = $CurrentHash
message = $_.Exception.Message
}
return
}
# Satır karşılaştırması
try {
$BaselineContent = Get-CleanFileContent -Path $BaselinePath
$CurrentContent = Get-CleanFileContent -Path $ConfigPath
$Differences = @(
Compare-Object `
-ReferenceObject $BaselineContent `
-DifferenceObject $CurrentContent `
-SyncWindow 3
)
}
catch {
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “COMPARE_ERROR”
severity = “critical”
file_path = $ConfigPath
baseline_hash = $BaselineHash
current_hash = $CurrentHash
snapshot_path = $SnapshotPath
message = $_.Exception.Message
}
return
}
$AddedLines = @(
$Differences |
Where-Object { $_.SideIndicator -eq “=>” } |
ForEach-Object { [string]$_.InputObject }
)
$RemovedLines = @(
$Differences |
Where-Object { $_.SideIndicator -eq “<=” } |
ForEach-Object { [string]$_.InputObject }
)
# CHANGED eventi
Write-IntegrityLog -SendToSplunk -Data @{
event_type = “iis_config_integrity”
status = “CHANGED”
severity = “high”
file_path = $ConfigPath
hash_algorithm = “SHA256”
baseline_hash = $BaselineHash
current_hash = $CurrentHash
snapshot_path = $SnapshotPath
added_line_count = $AddedLines.Count
removed_line_count = $RemovedLines.Count
added_lines = $AddedLines
removed_lines = $RemovedLines
}
# Aynı hash tekrar gönderilmesin
$CurrentHash |
Set-Content -Path $LastAlertedHashPath -Encoding ASCII -Force
return
3.3 CMD wrapper nereye konulacak?
Dosya adı: run_iis_config_integrity.cmd
Tam yol: C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\bin\run_iis_config_integrity.cmd
C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\bin\run_iis_config_integrity.cmd
Universal Forwarder, CMD wrapper’ı scripted input olarak çalıştırır. Wrapper, PowerShell scriptini NoProfile, NonInteractive ve ExecutionPolicy Bypass parametreleriyle çağırır.
@echo off
powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File “%~dp0iis_config_integrity.ps1”
exit /b %ERRORLEVEL%
3.4 inputs.conf nereye konulacak?
Tam yol: C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\local\inputs.conf
C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\local\inputs.conf
Bu stanza wrapper’ı 300 saniyede bir çalıştırır. İlk POC’de 60 saniye kullanılıp test tamamlandıktan sonra 300 saniyeye çekilebilir.
[script://.\bin\run_iis_config_integrity.cmd]
interval = 300
sourcetype = iis:config:integrity
index = iis_config_integrity
disabled = 0
3.5 app.conf nereye konulacak?
Tam yol: C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\local\app.conf
C:\Program Files\SplunkUniversalForwarder\etc\apps\TA-iis-config-integrity\local\app.conf
[install]
is_configured = 1
state = enabled
[ui]
is_visible = 0
[launcher]
author = Furkan KILINC
description = IIS configuration integrity monitoring
version = 1.0.0
[package]
id = TA-iis-config-integrity
3.6 UF app doğrulaması
$UFHome = “C:\Program Files\SplunkUniversalForwarder”
Test-Path “$UFHome\etc\apps\TA-iis-config-integrity\bin\iis_config_integrity.ps1”
Test-Path “$UFHome\etc\apps\TA-iis-config-integrity\bin\run_iis_config_integrity.cmd”
& “$UFHome\bin\splunk.exe” btool inputs list –debug |
Select-String “run_iis_config_integrity|iis_config_integrity|interval|disabled”
Restart-Service SplunkForwarder
Beklenen btool çıktısı:
[script://.\bin\run_iis_config_integrity.cmd]
disabled = 0
index = iis_config_integrity
interval = 300
sourcetype = iis:config:integrity


4. Standalone Splunk Sunucusunda Yapılacaklar
Standalone yapıda index ve parsing ayarları aynı Splunk Enterprise sunucusunda tutulur. POC ortamında Linux standalone Splunk kullanılmıştır.

4.1 Index tanımı
iis_config_integrity adında index tanımlanır
4.2 props.conf nereye konulacak?
Tam yol: /opt/splunk/etc/apps/TA-iis-config-integrity-receiver/local/props.conf
/opt/splunk/etc/apps/TA-iis-config-integrity-receiver/local/props.conf
[iis:config:integrity]
SHOULD_LINEMERGE = false
LINE_BREAKER = ([\r\n]+)
TRUNCATE = 0
KV_MODE = json
TIME_PREFIX = “timestamp”\s*:\s*”
TIME_FORMAT = %Y-%m-%dT%H:%M:%S.%7N%:z
MAX_TIMESTAMP_LOOKAHEAD = 40
KV_MODE=json search-time alan çıkarımı sağlar. Aynı sourcetype üzerinde INDEXED_EXTRACTIONS=json da tanımlanırsa alanlar iki kez görülebilir; bu nedenle tek yöntem kullanılmalıdır.
4.3 Standalone doğrulama
/opt/splunk/bin/splunk btool indexes list iis_config_integrity –debug
/opt/splunk/bin/splunk btool props list iis:config:integrity –debug
/opt/splunk/bin/splunk restart

Standalone Splunk üzerinde arama yapmadan önce IIS test sunucusunda kontrollü bir konfigürasyon değişikliği oluşturulmalıdır. Test sırasında applicationHost.config dosyasının elle düzenlenmesi yerine IIS’in desteklenen AppCmd aracı kullanılacaktır.
4.3.1 Mevcut konfigürasyonu baseline olarak kaydetme
Bu komutlar Windows IIS test sunucusunda, Administrator PowerShell üzerinden çalıştırılır.
$ConfigPath = "$env:windir\System32\inetsrv\config\applicationHost.config"
$BaselinePath = "C:\ProgramData\IISConfigMonitor\baseline\applicationHost.config"
$HashPath = "C:\ProgramData\IISConfigMonitor\baseline\applicationHost.config.sha256"
$StatePath = "C:\ProgramData\IISConfigMonitor\state\last_alerted_hash.txt"
Copy-Item `
-Path $ConfigPath `
-Destination $BaselinePath `
-Force
(
Get-FileHash `
-Path $ConfigPath `
-Algorithm SHA256
).Hash |
Set-Content `
-Path $HashPath `
-Encoding ASCII
Remove-Item `
-Path $StatePath `
-Force `
-ErrorAction SilentlyContinue
Bu işlem:
- Mevcut
applicationHost.configdosyasını onaylı baseline olarak kaydeder. - Baseline’a ait SHA-256 hash değerini günceller.
- Önceki testten kalan duplicate state dosyasını temizler.
Baseline ile aktif dosyanın aynı olduğunu doğrulamak için:
$CurrentHash = (
Get-FileHash `
-Path $ConfigPath `
-Algorithm SHA256
).Hash
$BaselineHash = (
Get-Content `
-Path $HashPath `
-Raw
).Trim()
[PSCustomObject]@{
CurrentHash = $CurrentHash
BaselineHash = $BaselineHash
Match = $CurrentHash -eq $BaselineHash
}
Beklenen sonuç:
Match : True

4.3.2 Değişiklik yok durumunun kontrol edilmesi
Universal Forwarder servisi yeniden başlatılır:
Restart-Service SplunkForwarder
Script, inputs.conf içindeki interval değerine göre çalışacaktır. Baseline ile aktif dosya aynı olduğu için Splunk’a CHANGED eventi gönderilmemelidir.
Kontrol sorgusu:
index=iis_config_integrity
sourcetype="iis:config:integrity"
earliest=-10m
| table _time host status current_hash added_line_count removed_line_count
| sort - _time
Bu aşamada yeni bir CHANGED sonucu oluşmaması beklenir. Script UNCHANGED olaylarını stdout üzerinden göndermediği için gereksiz lisans tüketimi oluşmaz.

4.3.3 Kontrollü IIS değişikliği oluşturma
Test amacıyla yeni bir application pool oluşturulur:
$AppCmd = "$env:windir\System32\inetsrv\appcmd.exe"
& $AppCmd add apppool /name:"FurkanIntegrityTest"
Beklenen çıktı:
APPPOOL object "FurkanIntegrityTest" added
Application pool’un oluştuğunu doğrulamak için:
& $AppCmd list apppool "FurkanIntegrityTest"
Bu işlem applicationHost.config dosyasına aşağıdakine benzer bir satır ekler:
<add name="FurkanIntegrityTest" />

Hash değerinin değiştiğini hemen doğrulayabiliriz:
$CurrentHash = (
Get-FileHash `
-Path $ConfigPath `
-Algorithm SHA256
).Hash
$BaselineHash = (
Get-Content `
-Path $HashPath `
-Raw
).Trim()
[PSCustomObject]@{
CurrentHash = $CurrentHash
BaselineHash = $BaselineHash
Changed = $CurrentHash -ne $BaselineHash
}
Beklenen sonuç:
Changed : True

4.3.4 Scripted input çalışmasının beklenmesi
inputs.conf içerisinde:
interval = 300
tanımlıysa en fazla beş dakika beklenir.
Test sırasında beklemek istenmiyorsa geçici olarak interval = 60 yapılabilir. Değişiklik sonrası Universal Forwarder restart edilmelidir:
Restart-Service SplunkForwarder
4.3.5 Splunk üzerinde CHANGED olayının doğrulanması
Standalone Splunk üzerinde aşağıdaki sorgu çalıştırılır:
index=iis_config_integrity
sourcetype=”iis:config:integrity”
earliest=-10m
| table _time host source status severity
baseline_hash current_hash
added_line_count removed_line_count
added_lines removed_lines snapshot_path
| sort – _time

4.3.6 Duplicate event kontrolü
İlk event geldikten sonra bir kontrol periyodu daha beklenir. Aynı config hash’i için yeniden event oluşmamalıdır.
index=iis_config_integrity
sourcetype=”iis:config:integrity”
earliest=-15m
| stats
count
earliest(_time) as first_seen
latest(_time) as last_seen
by host current_hash status
| convert ctime(first_seen) ctime(last_seen)

Bu sonuç last_alerted_hash.txt state mekanizmasının çalıştığını ve aynı değişikliğin her beş dakikada tekrar indexlenmediğini gösterir.
4.3.7 Silinen satır testinin yapılması
Önce test application pool mevcutken config baseline olarak kaydedilir:
Copy-Item `
-Path $ConfigPath `
-Destination $BaselinePath `
-Force
(
Get-FileHash `
-Path $ConfigPath `
-Algorithm SHA256
).Hash |
Set-Content `
-Path $HashPath `
-Encoding ASCII
Remove-Item `
-Path $StatePath `
-Force `
-ErrorAction SilentlyContinue
Ardından application pool silinir:
& $AppCmd delete apppool "FurkanIntegrityTest"
Beklenen çıktı:
APPPOOL object "FurkanIntegrityTest" deleted

Scriptin çalışması beklendikten sonra şu sorgu çalıştırılır:
index=iis_config_integrity
sourcetype="iis:config:integrity"
earliest=-10m
| table _time host status severity
added_line_count removed_line_count
added_lines removed_lines snapshot_path
| sort - _time
Beklenen sonuç:
status : CHANGED
added_line_count : 0
removed_line_count : 1
removed_lines : <add name="FurkanIntegrityTest" />

5. Cluster Mimarisinde Yapılacaklar
Cluster yapıda konfigürasyonlar görevlerine göre ayrılır. Universal Forwarder app’i Deployment Server’da, index tanımı Cluster Manager’da, search-time parsing ve alarm ise SHC Deployer’da tutulur.
| Katman | App | İçerik | Hedef |
| Deployment Server | TA-iis-config-integrity | PS1, CMD, inputs.conf | IIS UF sunucuları |
| Cluster Manager | TA-iis-config-integrity-indexers | indexes.conf | Indexer peers |
| SHC Deployer | TA-iis-config-integrity-search | props.conf | SHC üyeleri |
5.1 Deployment Server app’i nereye konulacak?
Sunucu: Deployment Server
Tam app yolu: /opt/splunk/etc/deployment-apps/TA-iis-config-integrity
/opt/splunk/etc/deployment-apps/TA-iis-config-integrity/
TA-iis-config-integrity
├── bin/
│ ├── iis_config_integrity.ps1
│ └── run_iis_config_integrity.cmd
└── local/
├── inputs.conf
└── app.conf
Standalone POC’de çalışan app, aynı içerikle deployment-apps altına taşınır. App yalnızca IIS hostlarını kapsayan ayrı bir server class üzerinden dağıtılmalıdır.
Önerilen server class: SC_IIS_Config_Integrity

5.2 Cluster Manager index tanımı
Cluster master üzerinde oluşturulan indexes.conf tanımı bundle ile indexer peer’lara dağıtılır.
5.3 SHC Deployer Search App’i
SHC Deployer üzerinde yalnızca JSON alanlarının Search Head Cluster üyelerinde çıkarılması için bir search app oluşturulur.
App yolu:
/opt/splunk/etc/shcluster/apps/TA-iis-config-integrity-search/
Klasör yapısı:
TA-iis-config-integrity-search
├── default
│ ├── app.conf
│ └── props.conf
└── metadata
└── default.meta
props.conf
Dosya yolu:
/opt/splunk/etc/shcluster/apps/TA-iis-config-integrity-search/default/props.conf
İçeriği:
[iis:config:integrity]
KV_MODE = json
Bu tanım sayesinde aşağıdaki JSON alanları Search Head üzerinde kullanılabilir:
status
severity
current_hash
baseline_hash
added_line_count
removed_line_count
added_lines
removed_lines
snapshot_path
app.conf
Dosya yolu:
/opt/splunk/etc/shcluster/apps/TA-iis-config-integrity-search/default/app.conf
İçeriği:
[install]
is_configured = 1
state = enabled
[ui]
is_visible = 0
[package]
id = TA-iis-config-integrity-search
default.meta
Dosya yolu:
/opt/splunk/etc/shcluster/apps/TA-iis-config-integrity-search/metadata/default.meta
İçeriği:
[]
access = read : [ * ], write : [ admin ]
export = system
SHC bundle dağıtımı
/opt/splunk/bin/splunk apply shcluster-bundle \
-target https://<SHC_MEMBER_IP>:8089 \
-auth admin
Dağıtım sonrasında JSON alanları Search Head Cluster üyelerinde kullanılabilir hale gelir.
Alarm ise Search Head arayüzünden manuel oluşturulur
index=iis_config_integrity sourcetype=”iis:config:integrity”
status IN (
“CHANGED”,
“FILE_NOT_FOUND”,
“INVALID_XML”,
“HASH_ERROR”,
“BASELINE_CREATE_ERROR”,
“BASELINE_READ_ERROR”,
“SNAPSHOT_ERROR”,
“COMPARE_ERROR”
)
| eval change_summary=case(
status=”CHANGED” AND added_line_count>0 AND removed_line_count>0,
“IIS config içerisinde ekleme ve silme tespit edildi”,
status=”CHANGED” AND added_line_count>0,
“IIS config içerisine yeni tanım eklendi”,
status=”CHANGED” AND removed_line_count>0,
“IIS config içerisinden tanım silindi”,
status=”FILE_NOT_FOUND”,
“IIS config dosyası bulunamadı”,
status=”INVALID_XML”,
“IIS config XML formatı geçersiz”,
status=”HASH_ERROR”,
“IIS config hash hesaplama hatası oluştu”,
status=”BASELINE_CREATE_ERROR”,
“IIS config baseline oluşturulamadı”,
status=”BASELINE_READ_ERROR”,
“IIS config baseline okunamadı”,
status=”SNAPSHOT_ERROR”,
“IIS config snapshot alınamadı”,
status=”COMPARE_ERROR”,
“IIS config karşılaştırma hatası oluştu”,
true(),
“IIS config integrity kontrol hatası”
)
| table _time host status severity change_summary file_path
baseline_hash current_hash
added_line_count removed_line_count
added_lines removed_lines
snapshot_path message
| sort – _time

6. Uçtan Uca Test Senaryosu
- Mevcut config’i baseline haline getirin.
$ConfigPath = “$env:windir\System32\inetsrv\config\applicationHost.config”
$BaselinePath = “C:\ProgramData\IISConfigMonitor\baseline\applicationHost.config”
$HashPath = “C:\ProgramData\IISConfigMonitor\baseline\applicationHost.config.sha256”
Copy-Item $ConfigPath $BaselinePath -Force
(Get-FileHash $ConfigPath -Algorithm SHA256).Hash |
Set-Content $HashPath -Encoding ASCII
2.Test application pool oluşturun.
$AppCmd = “$env:windir\System32\inetsrv\appcmd.exe”
& $AppCmd add apppool /name:”FurkanIntegrityTest”
3.En fazla beş dakika bekleyin.

4.Splunk üzerinde CHANGED eventini kontrol edin.
index=iis_config_integrity sourcetype=”iis:config:integrity”
| table _time host status severity added_line_count removed_line_count
added_lines removed_lines current_hash snapshot_path
| sort – _time

5.Aynı hash için duplicate oluşmadığını kontrol edin.
index=iis_config_integrity sourcetype=”iis:config:integrity”
| stats count earliest(_time) as first_seen latest(_time) as last_seen
by host current_hash status
| convert ctime(first_seen) ctime(last_seen)
Beklenen sonuç: aynı current_hash için count=1.
7. Disk Kullanımını Kontrol Altında Tutma
- Hash baseline ile aynıysa UNCHANGED eventi gönderilmez.
- Aynı current_hash daha önce gönderildiyse yeni CHANGED eventi oluşturulmaz.
- PowerShell debug ve Write-Host çıktıları kaldırılmıştır.
- JSON içerisinde yalnızca gerekli alanlar bulunur.
- Snapshot içeriği Splunk’a gönderilmez; yalnızca snapshot_path alanı gönderilir.
- Baseline, snapshot ve state klasörleri monitor input ile izlenmez.
30 günden eski snapshotları temizleme örneği:
Get-ChildItem “C:\ProgramData\IISConfigMonitor\snapshots” -File |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Force
8. Meşru Değişiklik Sonrası Baseline Güncelleme
Script değişikliği otomatik olarak baseline kabul etmez. Meşru değişiklik operasyon ekibi tarafından kontrol edildikten sonra aşağıdaki işlem uygulanabilir.
$ConfigPath = “$env:windir\System32\inetsrv\config\applicationHost.config”
$BaselinePath = “C:\ProgramData\IISConfigMonitor\baseline\applicationHost.config”
$HashPath = “C:\ProgramData\IISConfigMonitor\baseline\applicationHost.config.sha256”
$StatePath = “C:\ProgramData\IISConfigMonitor\state\last_alerted_hash.txt”
Copy-Item $ConfigPath $BaselinePath -Force
(Get-FileHash $ConfigPath -Algorithm SHA256).Hash |
Set-Content $HashPath -Encoding ASCII
Remove-Item $StatePath -Force -ErrorAction SilentlyContinue
Üretim ortamında baseline güncellemesi change ticket numarasıyla ilişkilendirilmeli ve tercihen ayrı bir audit event olarak Splunk’a gönderilmelidir.
9. Sonuç
Bu yapı IIS applicationHost.config dosyasını hash ve içerik seviyesinde takip eder. Yeni değişiklikte eklenen ve silinen satırlar Splunk’a gönderilir, snapshot alınır ve aynı hash için tekrar event oluşması engellenir. Standalone POC’de doğrulanan UF app’i üretim ortamında Deployment Server’dan dağıtılabilir; index Cluster Manager, parsing ve alarm ise SHC Deployer üzerinden yönetilebilir.
Share this content: