Active Directory Envanterini Tek Ekranda Görmek: Get-ADInfo

Merhaba

Devraldığınız bir Active Directory ortamında ilk 10 dakikada cevaplamanız gereken birkaç soru vardır:

  • Kaç hesap var?
  • Kaç sunucu, kaç iş istasyonu?
  • Forest hangi fonksiyonel seviyede?
  • Şema hangi sürüme kadar hazırlanmış?
  • FSMO (Flexible Single Master Operation) rolleri kimin üzerinde?

Bu soruların hepsinin cevabı zaten Active Directory’nin içinde duruyor. Sorun, onları toplamak için beş ayrı cmdlet, bir netdom ve bir de şema sürüm tablosu aramak zorunda kalmanız.

Bu yazıda, internette çokça dolaşan klasik “AD info” script’ini ele alıp ne yaptığını satır satır açıklayacağım. Ardından bu script’in üretimde sessizce yanlış sonuç verdiği üç noktayı göstereceğim, bunlar teorik değil, kendi lab ortamımda çalıştırırken karşıma çıkan gerçek hatalar. Son olarak düzeltilmiş ve Türkçeleştirilmiş halini paylaşacağım.

Klasik script ve ne yaptığı

Muhtemelen siz de bir yerlerde şuna benzer bir script görmüşsünüzdür:

$Computers = (Get-ADComputer -Filter *).count
$Workstations = (Get-ADComputer -LDAPFilter "(&(objectClass=Computer)(!operatingSystem=*server*))" -Searchbase (Get-ADDomain).distinguishedName).count
$Servers = (Get-ADComputer -LDAPFilter "(&(objectClass=Computer)(operatingSystem=*server*))" -Searchbase (Get-ADDomain).distinguishedName).count
$Users = (Get-ADUser -Filter *).count
$Groups = (Get-ADGroup -Filter *).Count
$ADForest = (Get-ADDomain).Forest
$FSMO = netdom query FSMO
$ADForestMode = (Get-ADForest).ForestMode
$ADDomainMode = (Get-ADDomain).DomainMode
$ADVer = Get-ADObject (Get-ADRootDSE).schemaNamingContext -property objectVersion | Select objectVersion
$ADNUM = $ADVer -replace "@{objectVersion=", "" -replace "}", ""

Mantığı basit ve doğru: nesneleri say, forest ve domain bilgilerini oku, şema sürümünü çek, netdom ile FSMO (Flexible Single Master Operation) rollerini listele. Sunucu ile iş istasyonu ayrımı operatingSystem özniteliğinde “server” kelimesi geçip geçmemesine bakılarak yapılıyor.

Fikir sağlam. Ama uygulamada üç yerde tökezliyor.

Sorun 1: Boş kalan satır

Lab ortamımda çalıştırdığımda çıktı şöyleydi:

Computers =    5
Workstions =
Servers =      4
Users =        3
Groups =       51

Workstations satırı boş. Oysa 5 bilgisayardan 4’ü sunucu, geriye 1 iş istasyonu kalması gerekiyor.

Sebep PowerShell’in koleksiyon davranışı. Bir cmdlet birden fazla nesne döndürdüğünde elinize dizi geçer ve .Count beklendiği gibi çalışır. Ama tek nesne döndüğünde PowerShell size diziyi değil, doğrudan nesnenin kendisini verir. Hiç sonuç yoksa $null döner. Her iki durumda da .Count ya boş kalır ya da yanıltıcı bir değer üretir.

Bu, envanter script’lerinde en tehlikeli hata sınıfıdır: script çökmez, hata vermez, sadece yanlış sayar. Rapora bakan kişi “demek ki hiç iş istasyonu yok” diye düşünür.

Çözüm @() ile sarmalamak:

$Bilgisayarlar = @(Get-ADComputer -Filter *).Count

@() ifadesi içindeki sonucu her koşulda diziye çevirir. Sonuç sıfır, bir veya bin olsun, .Count güvenilir şekilde çalışır.

Alışkanlık haline getirin: Sonucunu saydığınız her cmdlet çağrısını @() içine alın. Test ortamınızda 500 nesne döndüğü için sorun görmediğiniz bir script, müşterinin küçük ortamında tek nesne dönünce sessizce bozulur.

Sorun 2: Parantezsiz LDAP olumsuzlaması

İş istasyonu filtresine dikkat edin:

(&(objectClass=Computer)(!operatingSystem=*server*))

LDAP sözdiziminde olumsuzlama operatörü ! bir filtreyi sarmalar, bir karşılaştırmayı değil. Doğru yazım parantezlidir:

(&(objectClass=computer)(!(operatingSystem=*server*)))

Parantezsiz hali bazı domain controller’larda çalışır, bazılarında sözdizimi hatası verir. “Bende çalışıyordu” cümlesinin arkasındaki klasik sebeplerden biri budur. Standart yazıma geçmek, script’i taşınabilir hale getirir.

Sorun 3: Tanınmayan şema sürümü

Script’in şema tablosu şöyle bitiyordu:

If ($ADNum -eq '88') { $srv = 'Windows Server 2019/Windows Server 2022' }
ElseIf ($ADNum -eq '87') { $srv = 'Windows Server 2016' }
...

En üst değer 88. Windows Server 2025 domain controller üzerinde çalıştırdığımda çıktı şöyle oldu:

Active Directory Schema Version is 91 which corresponds to

Boş. Hiçbir ElseIf eşleşmediği için $srv değişkeni hiç atanmadı ve satır yarım kaldı. Yine hata yok, yine sessiz bir boşluk.

Microsoft’un güncel tablosu şöyle:

objectVersion İşletim sistemi
91 Windows Server 2025
88 Windows Server 2022
88 Windows Server 2019
87 Windows Server 2016
69 Windows Server 2012 R2
56 Windows Server 2012
47 Windows Server 2008 R2
44 Windows Server 2008 RTM
31 Windows Server 2003 R2
30 Windows Server 2003 RTM, Windows 2003 Service Pack 1, Windows 2003 Service Pack 2

Burada iki ayrıntı var.

Birincisi: Windows Server 2025’e geçişte şema tek adımda 91‘e çıkmıyor. Adprep sırasında 89 veya 90 ara değerleri de geçiliyor, nihai değer 91‘de duruyor. Eğer ortamınızda 89 veya 90 görüyorsanız, şema yükseltmesi yarım kalmış demektir, kontrol edilmesi gereken bir durumdur.

İkincisi: Windows Server 2019 ve Windows Server 2022 aynı şema sürümünü paylaşır. Bu ikisini objectVersion üzerinden birbirinden ayırt edemezsiniz. Ayrım için ayrı bir sorgu gerekir:

Get-ADDomainController -Filter * | Select-Object Name, OperatingSystem, OperatingSystemVersion

Uzun If/ElseIf zinciri yerine switch kullanmak hem okunaklı hem de genişletilebilir. En önemlisi default dalı sayesinde tanınmayan bir sürümde boş satır yerine sayının kendisini basar:

$SemaSurum = switch ($SemaNo) {
    91      { 'Windows Server 2025' }
    88      { 'Windows Server 2022 / Windows Server 2019' }
    87      { 'Windows Server 2016' }
    69      { 'Windows Server 2012 R2' }
    56      { 'Windows Server 2012' }
    47      { 'Windows Server 2008 R2' }
    44      { 'Windows Server 2008 RTM' }
    31      { 'Windows Server 2003 R2' }
    30      { 'Windows Server 2003 RTM / SP1 / SP2' }
    default { "Bilinmeyen sema surumu ($SemaNo)" }
}

Şema sürümü neyi söyler, neyi söylemez

Bu ayrımı netleştirmekte fayda var, çünkü sık karıştırılıyor.

Şema sürümü, domain controller’larınızın işletim sistemi sürümünü göstermez. Şemanın hangi sürüme kadar hazırlandığını gösterir. Şema 91 ise forest Windows Server 2025 için hazırlanmıştır; ancak ortamda hala Windows Server 2016 Domain Controller’lar bulunabilir. Yani bu bir “en üst sınır” bilgisidir, envanterin kendisi değildir.

Benzer şekilde şema sürümü ile fonksiyonel seviye de farklı şeylerdir. Şema 91 olup domain fonksiyonel seviyesi hala Windows2016Domain olabilir bu, yeni sürümle gelen özelliklerin (örneğin Windows Server 2025’in Kerberos ve LDAP sıkılaştırmaları) devrede olmadığı anlamına gelir.

Fonksiyonel seviye yükseltmesi geri alınamaz. Yükseltmeden önce tüm Domain Controller’ların işletim sistemi sürümünü doğrulayın ve bir sistem durumu (system state) yedeği alın.

Bir bulgu daha: kategorisiz bilgisayar nesneleri

Sayımları aynı kapsama çektikten sonra yeni bir kontrol imkanı doğuyor: iş istasyonu + sunucu toplamı, bilgisayar sayısına eşit olmalı. Eşit değilse ortada operatingSystem özniteliği boş olan nesneler var demektir.

Bunlar genellikle iki gruptan biridir: hiç domain’e katılıp açılmamış, önceden oluşturulmuş (pre-staged) bilgisayar hesapları; ya da uzun süre önce hizmet dışı kalmış, temizlenmemiş kalıntı kayıtlar. İkinci grup güvenlik açısından önemlidir, kullanılmayan ama canlı duran kimliklerdir.

Yeni script bu farkı otomatik hesaplayıp uyarı basıyor. Listelemek için:

Get-ADComputer -Filter * -Properties operatingSystem, whenCreated |
    Where-Object { -not $_.operatingSystem } |
    Select-Object Name, whenCreated, DistinguishedName

netdom bağımlılığı

Orijinal script FSMO (Flexible Single Master Operation) rollerini netdom query FSMO ile alıyor. Çıktısı okunaklı, bu yüzden tercih sebebi. Ancak iki kısıtı var: her ortamda kurulu olmayabilir ve uzaktan çalıştırmayı desteklemez. Script’i yönetim istasyonunuzdan uzak bir Domain Controller’ye yönlendirdiğinizde bu satır işe yaramaz.

Aynı bilgi zaten AD özniteliklerinde duruyor:

$Domain = Get-ADDomain
$Forest = Get-ADForest

$Forest.SchemaMaster
$Forest.DomainNamingMaster
$Domain.PDCEmulator
$Domain.RIDMaster
$Domain.InfrastructureMaster

Yeni script netdom varsa onu kullanıyor, yoksa bu yedek yola düşüyor. Böylece hem tanıdık çıktı korunuyor hem de uzaktan çalıştırma mümkün oluyor.

Sayımların kapsamı: sessiz bir tutarsızlık

Orijinal script’te gözden kaçan bir ayrıntı daha var. $Computers tüm dizini sayarken, $Workstations ve $Servers yalnızca domain DN’i altını sayıyor. Tek domainli bir ortamda fark etmez. Ama çoklu domain yapısında ya da belirli bir OU’yu incelerken toplamlar tutmaz ve yukarıdaki “kategorisiz nesne” kontrolü anlamsızlaşır. Yeni script tüm sayımları aynı -SearchBase üzerinden yapıyor.

Düzeltilmiş script

Tüm bu değişiklikleri içeren, Türkçe açıklamalı hali GitHub’da:

ActiveDirectory_Get-ADInfo

Özet olarak neler değişti:

  • Tüm sayımlar @() ile sarmalandı, tek veya sıfır sonuçta .Count güvenilir
  • LDAP (Lightweight Directory Access Protocol) olumsuzlaması parantezli standart yazıma geçirildi
  • Şema tablosuna Windows Server 2025 (91) eklendi, switch yapısına dönüştürüldü, default dalı ile tanınmayan sürümlerde sayı gösteriliyor
  • Tüm sayımlar aynı -SearchBase kapsamını kullanıyor
  • İş istasyonu + sunucu toplamı tutmadığında uyarı basılıyor
  • netdom bulunamazsa FSMO (Flexible Single Master Operation) bilgisi Active Directory özniteliklerinden üretiliyor
  • objectVersion doğrudan özellik olarak okunuyor, metin üzerinde -replace yapılmıyor
  • -Nesne parametresi ile çıktı PSCustomObject olarak alınabiliyor

Kullanımı:

# Tum domain envanteri, ekrana
.\Get-ADInfo.ps1

# Belirli bir OU
.\Get-ADInfo.ps1 -SearchBase "OU=Sirket,DC=domain,DC=local"

# Belirli bir DC uzerinden
.\Get-ADInfo.ps1 -Server "W25DC.domain.local"

# CSV'ye aktar
.\Get-ADInfo.ps1 -Nesne | Export-Csv .\ad-envanter.csv -NoTypeInformation -Encoding UTF8

Script salt okunurdur. Yalnızca LDAP sorgusu çalıştırır, hiçbir özniteliği değiştirmez. Standart domain kullanıcısı yetkisi yeterlidir, yönetici hakkı gerekmez. Üretim ortamında güvenle çalıştırılabilir.

Script’i çalıştırmak

Script’i indirdikten sonra bir klasöre koyun. Ben C:\scripts altında tutuyorum.
Çalıştırmak için:

  1. Bir Domain Controller üzerinde oturum açın.
  2. PowerShell 5.1 veya 7.x’i Run as administrator olarak çalıştırın.
  3. Dizin yolunu C:\scripts olarak değiştirin.
cd "C:\scripts"

Ardından script’i çalıştırın:

.\Get-ADInfo.ps1

İnternetten indirilen dosyalarda Windows’un engeline takılırsanız önce şunu çalıştırın:

Unblock-File .\Get-ADInfo.ps1

Çıktı şöyle görünür:

Burada okunacak birkaç şey var. Bilgisayar sayısı 5, bunun 4’ü sunucu ve 1’i iş istasyonu, toplam tutuyor, yani operatingSystem özniteliği boş kalmış kalıntı nesne yok. Forest ve domain fonksiyonel seviyeleri Windows2025ForestWindows2025Domain, şema sürümü de 91. Yani hem şema hazırlanmış hem de fonksiyonel seviye yükseltilmiş; bu ikisinin uyumlu olması istenen durumdur. FSMO (Flexible Single Master Operation) rollerinin tamamı tek DC üzerinde, ki bu lab ortamı için normaldir.

Script kullanmadan, tek seferlik komut

Dosya indirmek istemiyorsanız ya da müşteri ortamında script çalıştırma politikası varsa, aşağıdaki bloğu doğrudan PowerShell penceresine yapıştırabilirsiniz. Aynı çıktıyı üretir, diske hiçbir şey yazmaz:

Import-Module ActiveDirectory
$D = Get-ADDomain; $F = Get-ADForest; $SB = $D.DistinguishedName
$B = @(Get-ADComputer -SearchBase $SB -Filter *).Count
$I = @(Get-ADComputer -SearchBase $SB -LDAPFilter "(&(objectClass=computer)(!(operatingSystem=*server*)))").Count
$S = @(Get-ADComputer -SearchBase $SB -LDAPFilter "(&(objectClass=computer)(operatingSystem=*server*))").Count
$K = @(Get-ADUser  -SearchBase $SB -Filter *).Count
$G = @(Get-ADGroup -SearchBase $SB -Filter *).Count
$N = (Get-ADObject (Get-ADRootDSE).schemaNamingContext -Property objectVersion).objectVersion
$V = switch ($N) {
    91 {'Windows Server 2025'} 88 {'Windows Server 2022 / 2019'} 87 {'Windows Server 2016'}
    69 {'Windows Server 2012 R2'} 56 {'Windows Server 2012'} 47 {'Windows Server 2008 R2'}
    44 {'Windows Server 2008 RTM'} 31 {'Windows Server 2003 R2'} 30 {'Windows Server 2003'}
    default {"Bilinmeyen sema surumu ($N)"}
}
@(
  "", "Active Directory Envanteri", "Kapsam : $SB", ""
  "Bilgisayar    : $B", "  Is istasyonu: $I", "  Sunucu      : $S"
  "Kullanici     : $K", "Grup          : $G", ""
  "Forest adi      : $($D.Forest)", "Forest seviyesi : $($F.ForestMode)"
  "Domain seviyesi : $($D.DomainMode)", "Sema surumu     : $N ($V)", ""
  "FSMO Rol Sahipleri"
  "Schema master         $($F.SchemaMaster)"
  "Domain naming master  $($F.DomainNamingMaster)"
  "PDC                   $($D.PDCEmulator)"
  "RID pool manager      $($D.RIDMaster)"
  "Infrastructure master $($D.InfrastructureMaster)", ""
) | Write-Host -ForegroundColor Cyan

Bu blok FSMO (Flexible Single Master Operation) için netdom kullanmaz, bilgiyi doğrudan AD özniteliklerinden okur. Dolayısıyla RSAT (Remote Server Administration Tools)’ın yalnızca PowerShell modülü kurulu olan bir yönetim istasyonunda da çalışır.

Sonuç

Bu yazıdaki üç hatanın ortak özelliği şu: hiçbiri hata mesajı üretmiyor. Script çalışıyor, çıktı veriyor, her şey yolunda görünüyor. Sadece bir satır boş kalıyor ya da bir sayı eksik geliyor.

Envanter script’lerinde asıl risk çöken kod değil, sessizce yanlış sayan koddur. Çünkü çöken kodu fark edersiniz; yanlış sayanı rapora koyar, sunuma taşır, karar verirsiniz.

Bu yüzden default dalı, @() sarmalaması ve toplam kontrolü gibi küçük eklemeler, script’in kendisinden daha değerlidir.

Bir sonraki adım için: bu script envanteri gösterir, sağlığı değil. Domain controller’ların replikasyon, DNS, SYSVOL ve KRBTGT durumunu denetlemek için Get-ADHealth.ps1, parola hijyeni ve terk edilmiş hesaplar için Get-ADPwdLastSet.ps1 yazılarına göz atabilirsiniz.

Başka bir yazımızda görüşmek dileğiyle…


Kaynaklar

Bir yanıt yazın

Başa Dön