PowerShell ile Active Directory Group Policy Sonuçlarını Raporlama ve Analiz Etme

Büyük bir Active Directory ortamında Group Policy’lerin gerçekten uygulanıp uygulanmadığını kontrol etmek, her sysadmin’in zaman zaman başının ağrıdığı bir konudur. “GPO’yu bağladım ama neden çalışmıyor?” sorusu, özellikle yüzlerce OU ve binlerce makine içeren ortamlarda gerçek bir kabusa dönüşebilir. İşte tam bu noktada PowerShell devreye giriyor ve bize hem hızlı hem de tekrarlanabilir bir analiz imkânı sunuyor.

Bu yazıda, PowerShell kullanarak Group Policy sonuçlarını nasıl raporlayabileceğinizi, sorunları nasıl tespit edebileceğinizi ve tüm bu süreci nasıl otomatize edebileceğinizi gerçek dünya senaryolarıyla ele alacağız.

Temel Araçları Tanıyalım

PowerShell ile GPO raporlama yapabilmek için öncelikle iki temel modülün sisteminizde mevcut olması gerekiyor. Bunlar GroupPolicy modülü ve ActiveDirectory modülüdür. Her ikisi de Remote Server Administration Tools (RSAT) ile birlikte gelir.

# Modüllerin yüklü olup olmadığını kontrol et
Get-Module -ListAvailable | Where-Object { $_.Name -in @("GroupPolicy", "ActiveDirectory") }

# Eğer eksikse, Windows 10/11'de şu şekilde yükle
Add-WindowsCapability -Online -Name "Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0"
Add-WindowsCapability -Online -Name "Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0"

# Modülleri import et
Import-Module GroupPolicy
Import-Module ActiveDirectory

Bu adımı atlamayın. Modül yokken cmdlet çalıştırmaya çalışmak hata mesajlarıyla boşa vakit harcamanıza neden olur.

Ortamdaki Tüm GPO’ları Listeleme

İlk adım olarak domain’deki tüm GPO’ların genel bir envanterini çıkarmak iyi bir başlangıç noktasıdır. Bu size hem genel resmi gösterir hem de beklenmedik veya eski GPO’ları fark etmenizi sağlar.

# Tüm GPO'ları listele ve temel bilgileri al
Get-GPO -All | Select-Object DisplayName, Id, GpoStatus, CreationTime, ModificationTime |
    Sort-Object ModificationTime -Descending |
    Format-Table -AutoSize

# Sadece etkin (enabled) GPO'ları göster
Get-GPO -All | Where-Object { $_.GpoStatus -eq "AllSettingsEnabled" } |
    Select-Object DisplayName, CreationTime, ModificationTime |
    Sort-Object DisplayName

Burada dikkat etmeniz gereken GpoStatus alanıdır. Bir GPO’nun durumu şu değerleri alabilir:

  • AllSettingsEnabled: Hem bilgisayar hem kullanıcı ayarları aktif
  • UserSettingsDisabled: Sadece bilgisayar ayarları aktif
  • ComputerSettingsDisabled: Sadece kullanıcı ayarları aktif
  • AllSettingsDisabled: GPO tamamen devre dışı

Pratikte sıkça karşılaştığım bir durum şu: Yıllarca kullanılmış bir ortamda onlarca “AllSettingsDisabled” GPO bulunuyor ve kimse bunların ne işe yaradığını bilmiyor. Bu GPO’ları tespit edip ilgili ekiple değerlendirmeniz, gereksiz karmaşıklığı azaltır.

GPO Bağlantılarını (Links) Analiz Etme

GPO oluşturmak yetmez, doğru OU’ya bağlanmış olması gerekir. Bağlantı analizi yapmadan GPO sorunlarını çözmek neredeyse imkânsızdır.

# Belirli bir GPO'nun nereye bağlı olduğunu göster
function Get-GPOLinks {
    param(
        [string]$GPOName
    )
    
    $GPO = Get-GPO -Name $GPOName
    $Report = Get-GPOReport -Guid $GPO.Id -ReportType Xml
    $XmlReport = [xml]$Report
    
    $Links = $XmlReport.GPO.LinksTo
    
    if ($Links) {
        foreach ($Link in $Links) {
            [PSCustomObject]@{
                GPOName    = $GPOName
                LinkedTo   = $Link.SOMPath
                Enabled    = $Link.Enabled
                Enforced   = $Link.NoOverride
            }
        }
    } else {
        Write-Warning "$GPOName GPO'su herhangi bir yere bağlı değil!"
    }
}

# Kullanım
Get-GPOLinks -GPOName "Default Domain Policy"

# Tüm GPO'ların bağlantılarını tara
$AllGPOs = Get-GPO -All
foreach ($GPO in $AllGPOs) {
    Get-GPOLinks -GPOName $GPO.DisplayName
}

Bu script sayesinde bağlı olmayan GPO’ları hızlıca tespit edebilirsiniz. Bağlantısız GPO’lar gereksiz yere AD veritabanında yer kaplar ve bakım süreçlerini zorlaştırır.

Hedef Bilgisayar veya Kullanıcı için GPO Sonuçları (RSoP)

Gerçek dünya senaryolarında en sık ihtiyaç duyduğunuz şey şudur: “Bu bilgisayara hangi GPO’lar uygulanıyor ve bu ayar nereden geliyor?” Bunun için Get-GPResultantSetOfPolicy cmdlet’ini kullanırız.

# Yerel makine için RSoP raporu oluştur (HTML format)
Get-GPResultantSetOfPolicy -ReportType Html -Path "C:GPReportslocal_rsop.html"

# Uzak bir bilgisayar için RSoP raporu oluştur
Get-GPResultantSetOfPolicy -Computer "WORKSTATION01" -ReportType Html -Path "C:GPReportsws01_rsop.html"

# Belirli bir kullanıcı için RSoP raporu
Get-GPResultantSetOfPolicy -User "DOMAINahmet.yilmaz" -ReportType Html -Path "C:GPReportsuser_rsop.html"

# Hem bilgisayar hem kullanıcı için birlikte
Get-GPResultantSetOfPolicy -Computer "WORKSTATION01" -User "DOMAINahmet.yilmaz" -ReportType Html -Path "C:GPReportscombined_rsop.html"

Bu HTML raporlar oldukça kapsamlıdır. Ancak çok sayıda makine için rapor üretmeniz gerekiyorsa, bunu döngüye almanız ve çıktıyı daha analiz edilebilir bir formata dönüştürmeniz gerekir.

Toplu RSoP Raporlama: OU Bazlı Analiz

Bir OU altındaki tüm bilgisayarlar için GPO uygulama durumunu kontrol etmek istediğinizde şu yaklaşımı kullanabilirsiniz:

# Belirli bir OU'daki tüm bilgisayarlar için GPO raporu oluştur
function Invoke-BulkGPOReport {
    param(
        [string]$OUDistinguishedName,
        [string]$OutputPath = "C:GPReports",
        [int]$ThrottleLimit = 5
    )
    
    # Output klasörünü oluştur
    if (-not (Test-Path $OutputPath)) {
        New-Item -ItemType Directory -Path $OutputPath | Out-Null
    }
    
    # OU'daki aktif bilgisayarları al
    $Computers = Get-ADComputer -Filter { Enabled -eq $true } -SearchBase $OUDistinguishedName |
        Select-Object -ExpandProperty Name
    
    Write-Host "Toplam $($Computers.Count) bilgisayar bulundu." -ForegroundColor Cyan
    
    $Results = @()
    
    foreach ($Computer in $Computers) {
        Write-Host "Isleniyor: $Computer" -ForegroundColor Yellow
        
        # Bilgisayar erişilebilir mi kontrol et
        if (Test-Connection -ComputerName $Computer -Count 1 -Quiet) {
            try {
                $ReportPath = Join-Path $OutputPath "$Computer`_GPReport.html"
                Get-GPResultantSetOfPolicy -Computer $Computer -ReportType Html -Path $ReportPath -ErrorAction Stop
                
                $Results += [PSCustomObject]@{
                    ComputerName = $Computer
                    Status       = "Basarili"
                    ReportPath   = $ReportPath
                    Timestamp    = Get-Date
                }
            } catch {
                $Results += [PSCustomObject]@{
                    ComputerName = $Computer
                    Status       = "Hata: $($_.Exception.Message)"
                    ReportPath   = "N/A"
                    Timestamp    = Get-Date
                }
            }
        } else {
            $Results += [PSCustomObject]@{
                ComputerName = $Computer
                Status       = "Erisilemedim (Ping basarisiz)"
                ReportPath   = "N/A"
                Timestamp    = Get-Date
            }
        }
    }
    
    # Özet raporu CSV olarak kaydet
    $SummaryPath = Join-Path $OutputPath "GPReport_Summary_$(Get-Date -Format 'yyyyMMdd_HHmm').csv"
    $Results | Export-Csv -Path $SummaryPath -NoTypeInformation -Encoding UTF8
    
    Write-Host "`nOzet rapor kaydedildi: $SummaryPath" -ForegroundColor Green
    return $Results
}

# Kullanım örneği
Invoke-BulkGPOReport -OUDistinguishedName "OU=Workstations,OU=Istanbul,DC=firma,DC=local" -OutputPath "C:GPReportsIstanbul"

Bu script, büyük ortamlarda GPO denetimi yapmanın en pratik yollarından biridir. Özet CSV dosyası sayesinde hangi makinelere erişebildiğinizi, hangilerinde sorun olduğunu hızlıca görebilirsiniz.

GPO XML Raporundan Ayar Detaylarını Çekme

Bazen belirli bir ayarın hangi GPO’dan geldiğini veya bir GPO’nun içinde tam olarak ne olduğunu programatik olarak analiz etmek istersiniz. XML rapor formatı bu konuda çok güçlüdür.

# GPO içeriğini XML olarak al ve belirli ayarları sorgula
function Get-GPOSettings {
    param(
        [string]$GPOName,
        [string]$SettingSearch = ""
    )
    
    $GPO = Get-GPO -Name $GPOName
    $XmlContent = [xml](Get-GPOReport -Guid $GPO.Id -ReportType Xml)
    
    # Namespace manager tanımla
    $NSManager = New-Object System.Xml.XmlNamespaceManager($XmlContent.NameTable)
    $NSManager.AddNamespace("gp", "http://www.microsoft.com/GroupPolicy/Settings")
    
    Write-Host "`n=== $GPOName GPO Detaylari ===" -ForegroundColor Cyan
    Write-Host "Olusturulma: $($GPO.CreationTime)"
    Write-Host "Son Degisiklik: $($GPO.ModificationTime)"
    Write-Host "GPO ID: $($GPO.Id)"
    Write-Host "Durum: $($GPO.GpoStatus)"
    
    # Güvenlik filtrelemesini göster
    $SecurityFilter = $XmlContent.GPO.SecurityDescriptor.DACL.ACE |
        Where-Object { $_.Trustee.SID.'#text' -notmatch "S-1-1-0|S-1-5-9|S-1-5-18" }
    
    if ($SecurityFilter) {
        Write-Host "`nGuvenlik Filtrelemesi:" -ForegroundColor Yellow
        foreach ($ACE in $SecurityFilter) {
            Write-Host "  - $($ACE.Trustee.Name.'#text'): $($ACE.Standard.GPOGroupedAccessEnum)"
        }
    }
    
    # Ayar içeriği araması
    if ($SettingSearch -ne "") {
        $RawXml = Get-GPOReport -Guid $GPO.Id -ReportType Xml
        if ($RawXml -match $SettingSearch) {
            Write-Host "`n'$SettingSearch' ayari bu GPO'da mevcut!" -ForegroundColor Green
        } else {
            Write-Host "`n'$SettingSearch' ayari bu GPO'da bulunamadi." -ForegroundColor Red
        }
    }
}

# Kullanım
Get-GPOSettings -GPOName "Security Baseline Policy" -SettingSearch "PasswordComplexity"

GPO Uygulama Sorunlarını Tespit Etme: WMI Filtreleri ve Güvenlik Filtrelemesi

Pratikte en sık karşılaşılan GPO sorunlarından biri, güvenlik filtrelemesi veya WMI filtresi nedeniyle GPO’nun uygulanmamasıdır. Bu durumu PowerShell ile tespit edebilirsiniz.

# WMI filtresi olan GPO'ları listele
function Get-GPOWithWMIFilter {
    $AllGPOs = Get-GPO -All
    
    foreach ($GPO in $AllGPOs) {
        if ($GPO.WmiFilter) {
            [PSCustomObject]@{
                GPOName        = $GPO.DisplayName
                WMIFilterName  = $GPO.WmiFilter.Name
                WMIFilterQuery = $GPO.WmiFilter.Description
                GPOStatus      = $GPO.GpoStatus
            }
        }
    }
}

# Güvenlik filtrelemesi olan GPO'ları tespit et
function Get-GPOSecurityFiltering {
    $AllGPOs = Get-GPO -All
    $Results = @()
    
    foreach ($GPO in $AllGPOs) {
        $ACLs = Get-GPPermission -Guid $GPO.Id -All | 
            Where-Object { $_.Permission -eq "GpoApply" }
        
        foreach ($ACL in $ACLs) {
            $Results += [PSCustomObject]@{
                GPOName    = $GPO.DisplayName
                Principal  = $ACL.Trustee.Name
                TrusteeType = $ACL.Trustee.SidType
                Permission = $ACL.Permission
            }
        }
    }
    
    return $Results
}

# Sadece "Authenticated Users" dışında güvenlik filtrelemesi olanları göster
Get-GPOSecurityFiltering | Where-Object { $_.Principal -ne "Authenticated Users" } |
    Format-Table -AutoSize

# WMI filtreli GPO'ları kontrol et
$WMIFiltered = Get-GPOWithWMIFilter
if ($WMIFiltered) {
    Write-Host "WMI Filtresi olan GPO'lar:" -ForegroundColor Yellow
    $WMIFiltered | Format-Table -AutoSize
} else {
    Write-Host "Hicbir GPO'da WMI filtresi bulunamadi." -ForegroundColor Green
}

Bu script, özellikle “GPO bağlı ama çalışmıyor” diye gelen destek taleplerinde ilk koşturmanız gereken araçlardan biridir. Güvenlik filtrelemesinde sadece belirli bir grup varsa ve ilgili kullanıcı/bilgisayar o grubun üyesi değilse, GPO hiç uygulanmaz.

Periyodik GPO Sağlık Raporu Oluşturma

Tüm bu bilgileri bir araya getirerek düzenli çalışacak kapsamlı bir GPO sağlık raporu scripti yazalım. Bu scripti zamanlanmış görev olarak çalıştırabilir ve sonuçları e-posta ile alabilirsiniz.

# Kapsamlı GPO Sağlık Raporu
function Invoke-GPOHealthReport {
    param(
        [string]$OutputPath = "C:GPReports",
        [string]$SMTPServer = "",
        [string]$ReportEmail = "",
        [string]$FromEmail = ""
    )
    
    $ReportDate = Get-Date -Format "yyyy-MM-dd HH:mm"
    $ReportFile = Join-Path $OutputPath "GPO_Health_$(Get-Date -Format 'yyyyMMdd').txt"
    
    if (-not (Test-Path $OutputPath)) {
        New-Item -ItemType Directory -Path $OutputPath | Out-Null
    }
    
    $Report = @()
    $Report += "=" * 60
    $Report += "GROUP POLICY SAGLIK RAPORU"
    $Report += "Rapor Tarihi: $ReportDate"
    $Report += "Domain: $env:USERDNSDOMAIN"
    $Report += "=" * 60
    $Report += ""
    
    # 1. Genel GPO İstatistikleri
    $AllGPOs = Get-GPO -All
    $Report += "--- GENEL ISTATISTIKLER ---"
    $Report += "Toplam GPO Sayisi: $($AllGPOs.Count)"
    $Report += "Tamamen Aktif GPO: $(($AllGPOs | Where-Object {$_.GpoStatus -eq 'AllSettingsEnabled'}).Count)"
    $Report += "Tamamen Pasif GPO: $(($AllGPOs | Where-Object {$_.GpoStatus -eq 'AllSettingsDisabled'}).Count)"
    $Report += ""
    
    # 2. Bağlantısız GPO'lar (Risk oluşturmaz ama temizlenebilir)
    $Report += "--- BAGLANTISIZ GPO'LAR ---"
    $UnlinkedGPOs = @()
    
    foreach ($GPO in $AllGPOs) {
        $XmlReport = [xml](Get-GPOReport -Guid $GPO.Id -ReportType Xml)
        if (-not $XmlReport.GPO.LinksTo) {
            $UnlinkedGPOs += $GPO.DisplayName
            $Report += "  [UYARI] $($GPO.DisplayName) - Baglantisiz"
        }
    }
    
    if ($UnlinkedGPOs.Count -eq 0) {
        $Report += "  Tum GPO'lar en az bir yere baglanmis. (Iyi)"
    }
    $Report += ""
    
    # 3. Uzun süre değiştirilmemiş GPO'lar
    $Report += "--- 180 GUNDEN UZUN SUREDIR DEGISTIRILMEMIS GPO'LAR ---"
    $OldDate = (Get-Date).AddDays(-180)
    $OldGPOs = $AllGPOs | Where-Object { $_.ModificationTime -lt $OldDate }
    
    foreach ($GPO in $OldGPOs) {
        $DaysSince = [int]((Get-Date) - $GPO.ModificationTime).TotalDays
        $Report += "  $($GPO.DisplayName) - Son degisiklik: $($GPO.ModificationTime.ToString('yyyy-MM-dd')) ($DaysSince gun once)"
    }
    
    if ($OldGPOs.Count -eq 0) {
        $Report += "  Son 180 gunde tum GPO'lar guncellenmis."
    }
    $Report += ""
    
    # 4. WMI Filtresi Olan GPO'lar
    $Report += "--- WMI FILTRESI OLAN GPO'LAR ---"
    $WMIGPOs = $AllGPOs | Where-Object { $_.WmiFilter }
    foreach ($GPO in $WMIGPOs) {
        $Report += "  $($GPO.DisplayName) -> WMI: $($GPO.WmiFilter.Name)"
    }
    
    if ($WMIGPOs.Count -eq 0) {
        $Report += "  WMI filtresi olan GPO bulunamadi."
    }
    $Report += ""
    
    # Raporu kaydet
    $Report | Out-File -FilePath $ReportFile -Encoding UTF8
    Write-Host "Rapor kaydedildi: $ReportFile" -ForegroundColor Green
    
    # E-posta gönder (opsiyonel)
    if ($SMTPServer -ne "" -and $ReportEmail -ne "") {
        $Body = $Report -join "`n"
        Send-MailMessage -SmtpServer $SMTPServer -To $ReportEmail -From $FromEmail `
            -Subject "GPO Saglik Raporu - $ReportDate" -Body $Body -Encoding UTF8
        Write-Host "Rapor e-posta ile gonderildi: $ReportEmail" -ForegroundColor Green
    }
    
    return $ReportFile
}

# Kullanım
Invoke-GPOHealthReport -OutputPath "C:GPReports" -SMTPServer "mail.firma.local" -ReportEmail "[email protected]" -FromEmail "[email protected]"

Sonuç

PowerShell ile Group Policy analizi yapmak, hem günlük sorun çözme süreçlerinizi hem de periyodik denetim faaliyetlerinizi önemli ölçüde kolaylaştırır. Özellikle büyük ve karmaşık AD ortamlarında, elle yapılan kontrollerin yerini alan bu scriptler size hem zaman kazandırır hem de insan hatasından kaynaklanan gözden kaçırmaları minimize eder.

Pratikte öncelikle şu üç scripti düzenli olarak çalıştırmanızı öneririm: bağlantısız GPO kontrolü, güvenlik filtrelemesi analizi ve kapsamlı sağlık raporu. Bu üçü birlikte ortamınızın GPO sağlığı hakkında oldukça net bir resim çizer.

Scriptleri kendi ortamınıza uyarlarken OU yapınızı ve naming convention’larınızı göz önünde bulundurun. Her ortam farklıdır ve buradaki örnekler başlangıç noktası olarak değerlendirilmelidir. Özellikle e-posta bildirimlerini ve zamanlanmış görevleri devreye aldığınızda, GPO sorunlarını kullanıcılar fark etmeden önce tespit etme şansınız ciddi oranda artar.

Son olarak şunu söyleyelim: gpresult /h ve GPMC arayüzü hâlâ işe yarayan araçlardır. Ancak PowerShell scriptleri bunların önüne geçmez, aksine tamamlayıcı bir katman oluşturur. Manuel analiz ile otomatik raporlama birlikte kullanıldığında gerçek değerini gösterir.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir