VOLSYS Active Directory Rid Master Health Check


Bu script ile Active Directory Rıd Master Health Check kontrolü yaparak mevcut yapınızı analiz edebilirsiniz.

<#
Volsys
Vesion : 2.0
0fis : 13.3.2026
#>
[CmdletBinding()]
param(
[int]$DaysBack = 7,
[int]$EventCount = 30
)
$ErrorActionPreference = 'Stop'
try {
Import-Module ActiveDirectory -ErrorAction Stop
}
catch {
Write-Error "ActiveDirectory modülü yüklenemedi."
exit 1
}
# -------------------------------------------------------------------
# Output Paths
# -------------------------------------------------------------------
$TimeStamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$BaseFolder = "C:\Volsys\AD-Ultimate-Health"
$OutputFolder = Join-Path $BaseFolder $TimeStamp
$TxtReport = Join-Path $OutputFolder "Volsys_AD_Ultimate_HealthCheck_$TimeStamp.txt"
$HtmlReport = Join-Path $OutputFolder "Volsys_AD_Ultimate_HealthCheck_$TimeStamp.html"
New-Item -Path $OutputFolder -ItemType Directory -Force | Out-Null
# -------------------------------------------------------------------
# Containers
# -------------------------------------------------------------------
$Global:DashboardResults = New-Object System.Collections.Generic.List[object]
$Global:DetailSections = New-Object System.Collections.Generic.List[object]
# -------------------------------------------------------------------
# Logging
# -------------------------------------------------------------------
function Write-VolsysLog {
param(
[AllowEmptyString()]
[string]$Text
)
if ($null -eq $Text) { $Text = "" }
$Text | Out-File -FilePath $TxtReport -Append -Encoding UTF8
Write-Host $Text
}
function Write-Section {
param([string]$Title)
Write-VolsysLog ""
Write-VolsysLog ("=" * 100)
Write-VolsysLog ("{0,-100}" -f $Title)
Write-VolsysLog ("=" * 100)
}
function Write-SubSection {
param([string]$Title)
Write-VolsysLog ""
Write-VolsysLog ("-" * 100)
Write-VolsysLog ("{0,-100}" -f $Title)
Write-VolsysLog ("-" * 100)
}
# -------------------------------------------------------------------
# Dashboard Helpers
# -------------------------------------------------------------------
function Add-Result {
param(
[string]$Category,
[string]$Check,
[string]$Status,
[string]$Details
)
$obj = [PSCustomObject]@{
Category = $Category
Check = $Check
Status = $Status
Details = $Details
}
$Global:DashboardResults.Add($obj) | Out-Null
}
function Add-DetailSection {
param(
[string]$Title,
[string[]]$Lines
)
$obj = [PSCustomObject]@{
Title = $Title
Lines = $Lines
}
$Global:DetailSections.Add($obj) | Out-Null
}
function Safe-Run {
param(
[scriptblock]$ScriptBlock,
[string]$OnError = "Command failed."
)
try {
& $ScriptBlock
}
catch {
Write-VolsysLog "$OnError Error: $($_.Exception.Message)"
Add-Result -Category "Execution" -Check $OnError -Status "FAIL" -Details $_.Exception.Message
}
}
# -------------------------------------------------------------------
# Utility Functions
# -------------------------------------------------------------------
function Convert-RIDBlockState {
param($Value)
if ($null -eq $Value) { return "Not Set / Normal" }
elseif ($Value -eq $false) { return "FALSE - RID allocation blocked" }
elseif ($Value -eq $true) { return "TRUE - Manual override may have been applied" }
else { return "$Value" }
}
function Convert-IntervalToRange {
param([Int64]$Value)
$LowPart = $Value -band 0xFFFFFFFF
$HighPart = $Value -shr 32
[PSCustomObject]@{
LowPart = $LowPart
HighPart = $HighPart
Count = ($HighPart - $LowPart + 1)
}
}
function Get-StatusClass {
param([string]$Status)
switch ($Status.ToUpper()) {
'PASS' { return 'pass' }
'WARN' { return 'warn' }
'FAIL' { return 'fail' }
default { return 'info' }
}
}
function ConvertTo-HtmlSafe {
param([string]$Text)
if ($null -eq $Text) { return "" }
return [System.Net.WebUtility]::HtmlEncode($Text)
}
# -------------------------------------------------------------------
# Initial Header
# -------------------------------------------------------------------
Write-VolsysLog ("=" * 100)
Write-VolsysLog ("{0,-100}" -f "VOLSYS AD ULTIMATE HEALTH CHECK - HTML DASHBOARD")
Write-VolsysLog ("=" * 100)
Write-VolsysLog "Report Time : $(Get-Date)"
Write-VolsysLog "Computer Name : $env:COMPUTERNAME"
Write-VolsysLog "User : $env:USERNAME"
Write-VolsysLog "DaysBack : $DaysBack"
Write-VolsysLog "EventCount : $EventCount"
Write-VolsysLog "TXT Report : $TxtReport"
Write-VolsysLog "HTML Report : $HtmlReport"
# -------------------------------------------------------------------
# Forest / Domain
# -------------------------------------------------------------------
$Forest = $null
$Domain = $null
$DCs = @()
$DomainDN = $null
Write-Section "FOREST AND DOMAIN SUMMARY"
Safe-Run -OnError "Forest/Domain bilgisi alinamadi." -ScriptBlock {
$Forest = Get-ADForest
$Domain = Get-ADDomain
$DomainDN = $Domain.DistinguishedName
Write-VolsysLog "Forest Name : $($Forest.Name)"
Write-VolsysLog "Forest Mode : $($Forest.ForestMode)"
Write-VolsysLog "Root Domain : $($Forest.RootDomain)"
Write-VolsysLog "Domains : $($Forest.Domains -join ', ')"
Write-VolsysLog "Sites : $($Forest.Sites -join ', ')"
Write-VolsysLog "Domain FQDN : $($Domain.DNSRoot)"
Write-VolsysLog "NetBIOS Name : $($Domain.NetBIOSName)"
Write-VolsysLog "Domain Mode : $($Domain.DomainMode)"
Write-VolsysLog "Domain DN : $($Domain.DistinguishedName)"
Add-Result -Category "Forest" -Check "Forest discovery" -Status "PASS" -Details "Forest: $($Forest.Name)"
Add-Result -Category "Domain" -Check "Domain discovery" -Status "PASS" -Details "Domain: $($Domain.DNSRoot)"
Add-DetailSection -Title "Forest and Domain Summary" -Lines @(
"Forest Name: $($Forest.Name)",
"Forest Mode: $($Forest.ForestMode)",
"Root Domain: $($Forest.RootDomain)",
"Domains: $($Forest.Domains -join ', ')",
"Sites: $($Forest.Sites -join ', ')",
"Domain FQDN: $($Domain.DNSRoot)",
"NetBIOS Name: $($Domain.NetBIOSName)",
"Domain Mode: $($Domain.DomainMode)",
"Domain DN: $($Domain.DistinguishedName)"
)
}
# -------------------------------------------------------------------
# FSMO
# -------------------------------------------------------------------
Write-Section "FSMO ROLE OWNERS"
Safe-Run -OnError "FSMO role bilgileri alinamadi." -ScriptBlock {
Write-VolsysLog "Schema Master : $($Forest.SchemaMaster)"
Write-VolsysLog "Domain Naming Master : $($Forest.DomainNamingMaster)"
Write-VolsysLog "PDC Emulator : $($Domain.PDCEmulator)"
Write-VolsysLog "RID Master : $($Domain.RIDMaster)"
Write-VolsysLog "Infrastructure Master : $($Domain.InfrastructureMaster)"
Add-Result -Category "FSMO" -Check "FSMO roles readable" -Status "PASS" -Details "FSMO role owners successfully collected"
Add-DetailSection -Title "FSMO Role Owners" -Lines @(
"Schema Master: $($Forest.SchemaMaster)",
"Domain Naming Master: $($Forest.DomainNamingMaster)",
"PDC Emulator: $($Domain.PDCEmulator)",
"RID Master: $($Domain.RIDMaster)",
"Infrastructure Master: $($Domain.InfrastructureMaster)"
)
}
# -------------------------------------------------------------------
# Domain Controllers
# -------------------------------------------------------------------
Write-Section "DOMAIN CONTROLLERS"
Safe-Run -OnError "Domain Controller listesi alinamadi." -ScriptBlock {
$DCs = Get-ADDomainController -Filter * | Sort-Object HostName
if ($DCs.Count -eq 0) {
Add-Result -Category "DC" -Check "Domain Controllers found" -Status "FAIL" -Details "No DCs returned"
}
else {
Add-Result -Category "DC" -Check "Domain Controllers found" -Status "PASS" -Details "$($DCs.Count) DC(s) found"
}
$dcLines = @()
foreach ($DC in $DCs) {
Write-VolsysLog ""
Write-VolsysLog "DC HostName : $($DC.HostName)"
Write-VolsysLog "IPv4 Address : $($DC.IPv4Address)"
Write-VolsysLog "Site : $($DC.Site)"
Write-VolsysLog "OS Version : $($DC.OperatingSystem)"
Write-VolsysLog "Is Global Catalog : $($DC.IsGlobalCatalog)"
Write-VolsysLog "Is ReadOnly : $($DC.IsReadOnly)"
$dcLines += "DC: $($DC.HostName) | IP: $($DC.IPv4Address) | Site: $($DC.Site) | OS: $($DC.OperatingSystem) | GC: $($DC.IsGlobalCatalog) | RODC: $($DC.IsReadOnly)"
}
Add-DetailSection -Title "Domain Controllers" -Lines $dcLines
}
# -------------------------------------------------------------------
# RID Health
# -------------------------------------------------------------------
Write-Section "RID HEALTH"
Safe-Run -OnError "RID health bilgileri alinamadi." -ScriptBlock {
$RidManager = Get-ADObject `
-Identity "CN=RID Manager$,CN=System,$DomainDN" `
-Properties rIDAvailablePool, msDS-RIDPoolAllocationEnabled
$GlobalPool = Convert-IntervalToRange -Value ([Int64]$RidManager.rIDAvailablePool)
$DefaultMaxRID = 1073741823
$RemainingRIDs = [int64]$GlobalPool.Count
$UsedRIDs = $DefaultMaxRID - $RemainingRIDs
if ($UsedRIDs -lt 0) { $UsedRIDs = 0 }
$UsedPercent = [math]::Round((($UsedRIDs / $DefaultMaxRID) * 100), 4)
$RemainingPercent = [math]::Round((($RemainingRIDs / $DefaultMaxRID) * 100), 4)
$BlockState = Convert-RIDBlockState -Value $RidManager.'msDS-RIDPoolAllocationEnabled'
Write-VolsysLog "RID Master : $($Domain.RIDMaster)"
Write-VolsysLog "RID Pool Raw : $($RidManager.rIDAvailablePool)"
Write-VolsysLog "RID Low Part : $($GlobalPool.LowPart)"
Write-VolsysLog "RID High Part : $($GlobalPool.HighPart)"
Write-VolsysLog "Remaining RIDs : $RemainingRIDs"
Write-VolsysLog "Used RIDs : $UsedRIDs"
Write-VolsysLog "Used Percent : $UsedPercent %"
Write-VolsysLog "Remaining Percent : $RemainingPercent %"
Write-VolsysLog "RID Allocation State : $BlockState"
if ($RidManager.'msDS-RIDPoolAllocationEnabled' -eq $false) {
Add-Result -Category "RID" -Check "RID allocation state" -Status "FAIL" -Details "RID allocation blocked"
}
elseif ($RemainingPercent -le 15) {
Add-Result -Category "RID" -Check "RID remaining percentage" -Status "WARN" -Details "Remaining RID percentage is low: $RemainingPercent %"
}
else {
Add-Result -Category "RID" -Check "RID health" -Status "PASS" -Details "Remaining RID percentage: $RemainingPercent %"
}
Add-DetailSection -Title "RID Health" -Lines @(
"RID Master: $($Domain.RIDMaster)",
"RID Pool Raw: $($RidManager.rIDAvailablePool)",
"RID Low Part: $($GlobalPool.LowPart)",
"RID High Part: $($GlobalPool.HighPart)",
"Remaining RIDs: $RemainingRIDs",
"Used RIDs: $UsedRIDs",
"Used Percent: $UsedPercent %",
"Remaining Percent: $RemainingPercent %",
"RID Allocation State: $BlockState"
)
}
# -------------------------------------------------------------------
# Replication
# -------------------------------------------------------------------
Write-Section "REPLICATION HEALTH"
Safe-Run -OnError "Replication summary alinamadi." -ScriptBlock {
$repSummary = & repadmin.exe /replsummary 2>&1
$repText = $repSummary | Out-String
foreach ($line in $repSummary) {
Write-VolsysLog "$line"
}
if ($repText -match '\bfails\b' -or $repText -match '\berror\b') {
Add-Result -Category "Replication" -Check "repadmin /replsummary" -Status "WARN" -Details "Replication summary contains failure/error text"
}
else {
Add-Result -Category "Replication" -Check "repadmin /replsummary" -Status "PASS" -Details "No obvious replication failure text detected"
}
Add-DetailSection -Title "Replication Summary" -Lines ($repSummary | ForEach-Object { "$_" })
}
# -------------------------------------------------------------------
# DCDIAG Tests
# -------------------------------------------------------------------
Write-Section "DCDIAG TESTS"
Safe-Run -OnError "dcdiag DNS testi alinamadi." -ScriptBlock {
$dcdiagDns = & dcdiag.exe /e /test:DNS 2>&1
$text = $dcdiagDns | Out-String
foreach ($line in $dcdiagDns) { Write-VolsysLog "$line" }
if ($text -match 'failed test' -or $text -match 'error') {
Add-Result -Category "DCDIAG" -Check "DCDIAG DNS" -Status "WARN" -Details "DNS test contains failure/error text"
}
else {
Add-Result -Category "DCDIAG" -Check "DCDIAG DNS" -Status "PASS" -Details "No obvious DNS failure text detected"
}
Add-DetailSection -Title "DCDIAG DNS" -Lines ($dcdiagDns | ForEach-Object { "$_" })
}
Safe-Run -OnError "dcdiag SYSVOL testi alinamadi." -ScriptBlock {
$dcdiagSysvol = & dcdiag.exe /e /test:sysvolcheck 2>&1
$text = $dcdiagSysvol | Out-String
foreach ($line in $dcdiagSysvol) { Write-VolsysLog "$line" }
if ($text -match 'failed test' -or $text -match 'error') {
Add-Result -Category "DCDIAG" -Check "DCDIAG SYSVOL" -Status "WARN" -Details "SYSVOL test contains failure/error text"
}
else {
Add-Result -Category "DCDIAG" -Check "DCDIAG SYSVOL" -Status "PASS" -Details "No obvious SYSVOL failure text detected"
}
Add-DetailSection -Title "DCDIAG SYSVOL" -Lines ($dcdiagSysvol | ForEach-Object { "$_" })
}
Safe-Run -OnError "dcdiag RID testi alinamadi." -ScriptBlock {
$dcdiagRid = & dcdiag.exe /test:ridmanager /v 2>&1
$text = $dcdiagRid | Out-String
foreach ($line in $dcdiagRid) { Write-VolsysLog "$line" }
if ($text -match 'failed test' -or $text -match 'error') {
Add-Result -Category "DCDIAG" -Check "DCDIAG RID Manager" -Status "WARN" -Details "RID Manager test contains failure/error text"
}
else {
Add-Result -Category "DCDIAG" -Check "DCDIAG RID Manager" -Status "PASS" -Details "No obvious RID Manager failure text detected"
}
Add-DetailSection -Title "DCDIAG RID Manager" -Lines ($dcdiagRid | ForEach-Object { "$_" })
}
# -------------------------------------------------------------------
# Per-DC Checks
# -------------------------------------------------------------------
Write-Section "PER-DC DETAILED CHECKS"
foreach ($DC in $DCs) {
Write-SubSection "DC DETAIL - $($DC.HostName)"
$sectionLines = New-Object System.Collections.Generic.List[string]
Safe-Run -OnError "DC detaylari alinamadi: $($DC.HostName)" -ScriptBlock {
Write-VolsysLog "HostName : $($DC.HostName)"
Write-VolsysLog "Site : $($DC.Site)"
Write-VolsysLog "IPv4 : $($DC.IPv4Address)"
Write-VolsysLog "Is Global Catalog : $($DC.IsGlobalCatalog)"
Write-VolsysLog "Is ReadOnly : $($DC.IsReadOnly)"
$sectionLines.Add("HostName: $($DC.HostName)")
$sectionLines.Add("Site: $($DC.Site)")
$sectionLines.Add("IPv4: $($DC.IPv4Address)")
$sectionLines.Add("Is Global Catalog: $($DC.IsGlobalCatalog)")
$sectionLines.Add("Is ReadOnly: $($DC.IsReadOnly)")
# Ping
try {
$pingOk = Test-Connection -ComputerName $DC.HostName -Count 2 -Quiet -ErrorAction Stop
if ($pingOk) {
Add-Result -Category "Connectivity" -Check "Ping $($DC.HostName)" -Status "PASS" -Details "Host reachable"
}
else {
Add-Result -Category "Connectivity" -Check "Ping $($DC.HostName)" -Status "FAIL" -Details "Host unreachable"
}
Write-VolsysLog "Ping Reachable : $pingOk"
$sectionLines.Add("Ping Reachable: $pingOk")
}
catch {
Add-Result -Category "Connectivity" -Check "Ping $($DC.HostName)" -Status "FAIL" -Details $_.Exception.Message
Write-VolsysLog "Ping Check Error : $($_.Exception.Message)"
$sectionLines.Add("Ping Check Error: $($_.Exception.Message)")
}
# Shares
try {
$shares = Get-WmiObject Win32_Share -ComputerName $DC.HostName
$sysvolFound = $shares | Where-Object { $_.Name -eq 'SYSVOL' }
$netlogonFound = $shares | Where-Object { $_.Name -eq 'NETLOGON' }
Write-VolsysLog "SYSVOL Share Present : $([bool]$sysvolFound)"
Write-VolsysLog "NETLOGON Share Present : $([bool]$netlogonFound)"
$sectionLines.Add("SYSVOL Share Present: $([bool]$sysvolFound)")
$sectionLines.Add("NETLOGON Share Present: $([bool]$netlogonFound)")
if ($sysvolFound -and $netlogonFound) {
Add-Result -Category "SYSVOL" -Check "$($DC.HostName) SYSVOL/NETLOGON shares" -Status "PASS" -Details "Both shares present"
}
else {
Add-Result -Category "SYSVOL" -Check "$($DC.HostName) SYSVOL/NETLOGON shares" -Status "FAIL" -Details "One or both shares missing"
}
}
catch {
Add-Result -Category "SYSVOL" -Check "$($DC.HostName) SYSVOL/NETLOGON shares" -Status "WARN" -Details $_.Exception.Message
Write-VolsysLog "Share Check Error : $($_.Exception.Message)"
$sectionLines.Add("Share Check Error: $($_.Exception.Message)")
}
# Services
try {
$serviceNames = 'NTDS','DNS','DFSR','KDC','W32Time','Netlogon'
$services = Get-Service -ComputerName $DC.HostName -Name $serviceNames -ErrorAction SilentlyContinue
foreach ($svc in $services | Sort-Object Name) {
Write-VolsysLog ("Service {0,-25}: {1}" -f $svc.Name, $svc.Status)
$sectionLines.Add(("Service {0}: {1}" -f $svc.Name, $svc.Status))
if ($svc.Status -eq 'Running') {
Add-Result -Category "Services" -Check "$($DC.HostName) service $($svc.Name)" -Status "PASS" -Details "Running"
}
else {
Add-Result -Category "Services" -Check "$($DC.HostName) service $($svc.Name)" -Status "WARN" -Details "Status: $($svc.Status)"
}
}
}
catch {
Add-Result -Category "Services" -Check "$($DC.HostName) service query" -Status "WARN" -Details $_.Exception.Message
Write-VolsysLog "Service Check Error : $($_.Exception.Message)"
$sectionLines.Add("Service Check Error: $($_.Exception.Message)")
}
# Time
try {
$w32status = & w32tm.exe /monitor /computers:$($DC.HostName) 2>&1
foreach ($line in $w32status) {
Write-VolsysLog "$line"
$sectionLines.Add("$line")
}
Add-Result -Category "Time" -Check "W32Time monitor $($DC.HostName)" -Status "INFO" -Details "Review time monitor output"
}
catch {
Add-Result -Category "Time" -Check "W32Time monitor $($DC.HostName)" -Status "WARN" -Details $_.Exception.Message
Write-VolsysLog "Time Check Error : $($_.Exception.Message)"
$sectionLines.Add("Time Check Error: $($_.Exception.Message)")
}
}
Add-DetailSection -Title "DC Detail - $($DC.HostName)" -Lines ($sectionLines.ToArray())
}
# -------------------------------------------------------------------
# Event Checks
# -------------------------------------------------------------------
Write-Section "EVENT HEALTH CHECKS"
foreach ($DC in $DCs) {
# DFSR
Safe-Run -OnError "DFSR eventleri alinamadi: $($DC.HostName)" -ScriptBlock {
$dfsrEvents = Get-WinEvent -ComputerName $DC.HostName -FilterHashtable @{
LogName = 'DFS Replication'
StartTime = (Get-Date).AddDays(-$DaysBack)
} -MaxEvents $EventCount
$lines = @()
if ($dfsrEvents) {
foreach ($evt in $dfsrEvents) {
$msg = ($evt.Message -replace "`r`n", ' ') -replace '\s{2,}', ' '
$lines += "[{0}] ID={1} Level={2} Message={3}" -f $evt.TimeCreated, $evt.Id, $evt.LevelDisplayName, $msg
}
Add-Result -Category "DFSR" -Check "$($DC.HostName) DFSR events" -Status "INFO" -Details "$($dfsrEvents.Count) DFSR events collected"
}
else {
$lines += "No DFSR events found."
Add-Result -Category "DFSR" -Check "$($DC.HostName) DFSR events" -Status "PASS" -Details "No DFSR events found in selected range"
}
Add-DetailSection -Title "DFSR Events - $($DC.HostName)" -Lines $lines
}
# DNS Server
Safe-Run -OnError "DNS eventleri alinamadi: $($DC.HostName)" -ScriptBlock {
$dnsEvents = Get-WinEvent -ComputerName $DC.HostName -FilterHashtable @{
LogName = 'DNS Server'
StartTime = (Get-Date).AddDays(-$DaysBack)
} -MaxEvents $EventCount
$lines = @()
if ($dnsEvents) {
foreach ($evt in $dnsEvents) {
$msg = ($evt.Message -replace "`r`n", ' ') -replace '\s{2,}', ' '
$lines += "[{0}] ID={1} Level={2} Message={3}" -f $evt.TimeCreated, $evt.Id, $evt.LevelDisplayName, $msg
}
Add-Result -Category "DNS Events" -Check "$($DC.HostName) DNS events" -Status "INFO" -Details "$($dnsEvents.Count) DNS Server events collected"
}
else {
$lines += "No DNS Server events found."
Add-Result -Category "DNS Events" -Check "$($DC.HostName) DNS events" -Status "PASS" -Details "No DNS Server events found in selected range"
}
Add-DetailSection -Title "DNS Events - $($DC.HostName)" -Lines $lines
}
# Directory Service
Safe-Run -OnError "Directory Service eventleri alinamadi: $($DC.HostName)" -ScriptBlock {
$adEvents = Get-WinEvent -ComputerName $DC.HostName -FilterHashtable @{
LogName = 'Directory Service'
StartTime = (Get-Date).AddDays(-$DaysBack)
} -MaxEvents $EventCount
$lines = @()
if ($adEvents) {
foreach ($evt in $adEvents) {
$msg = ($evt.Message -replace "`r`n", ' ') -replace '\s{2,}', ' '
$lines += "[{0}] ID={1} Level={2} Message={3}" -f $evt.TimeCreated, $evt.Id, $evt.LevelDisplayName, $msg
}
Add-Result -Category "AD Events" -Check "$($DC.HostName) Directory Service events" -Status "INFO" -Details "$($adEvents.Count) Directory Service events collected"
}
else {
$lines += "No Directory Service events found."
Add-Result -Category "AD Events" -Check "$($DC.HostName) Directory Service events" -Status "PASS" -Details "No Directory Service events found in selected range"
}
Add-DetailSection -Title "Directory Service Events - $($DC.HostName)" -Lines $lines
}
}
# -------------------------------------------------------------------
# Object Creation Trend
# -------------------------------------------------------------------
Write-Section "OBJECT CREATION TREND"
Safe-Run -OnError "Obje trend bilgisi alinamadi." -ScriptBlock {
$Since = (Get-Date).AddDays(-$DaysBack)
$ldapTime = $Since.ToUniversalTime().ToString("yyyyMMddHHmmss.0Z")
$RecentUsers = @(Get-ADUser -LDAPFilter "(whenCreated>=$ldapTime)" -Properties whenCreated)
$RecentComputers = @(Get-ADComputer -LDAPFilter "(whenCreated>=$ldapTime)" -Properties whenCreated)
$RecentGroups = @(Get-ADGroup -LDAPFilter "(whenCreated>=$ldapTime)" -Properties whenCreated)
$totalRecent = $RecentUsers.Count + $RecentComputers.Count + $RecentGroups.Count
Write-VolsysLog "Since : $Since"
Write-VolsysLog "Users Created : $($RecentUsers.Count)"
Write-VolsysLog "Computers Created : $($RecentComputers.Count)"
Write-VolsysLog "Groups Created : $($RecentGroups.Count)"
Write-VolsysLog "Total Security Objects : $totalRecent"
if ($totalRecent -ge 1000) {
Add-Result -Category "Object Trend" -Check "Recent object creation volume" -Status "WARN" -Details "High object creation volume detected: $totalRecent"
}
elseif ($totalRecent -ge 250) {
Add-Result -Category "Object Trend" -Check "Recent object creation volume" -Status "WARN" -Details "Elevated object creation volume detected: $totalRecent"
}
else {
Add-Result -Category "Object Trend" -Check "Recent object creation volume" -Status "PASS" -Details "Normal object creation volume: $totalRecent"
}
Add-DetailSection -Title "Object Creation Trend" -Lines @(
"Since: $Since",
"Users Created: $($RecentUsers.Count)",
"Computers Created: $($RecentComputers.Count)",
"Groups Created: $($RecentGroups.Count)",
"Total Security Objects: $totalRecent"
)
}
# -------------------------------------------------------------------
# HTML Report
# -------------------------------------------------------------------
Write-Section "GENERATING HTML DASHBOARD"
$passCount = @($DashboardResults | Where-Object { $_.Status -eq 'PASS' }).Count
$warnCount = @($DashboardResults | Where-Object { $_.Status -eq 'WARN' }).Count
$failCount = @($DashboardResults | Where-Object { $_.Status -eq 'FAIL' }).Count
$infoCount = @($DashboardResults | Where-Object { $_.Status -eq 'INFO' }).Count
$totalCount = $DashboardResults.Count
$overallStatus = if ($failCount -gt 0) { "FAIL" } elseif ($warnCount -gt 0) { "WARN" } else { "PASS" }
$html = New-Object System.Text.StringBuilder
[void]$html.AppendLine("<!DOCTYPE html>")
[void]$html.AppendLine("<html lang='en'>")
[void]$html.AppendLine("<head>")
[void]$html.AppendLine("<meta charset='utf-8'>")
[void]$html.AppendLine("<meta name='viewport' content='width=device-width, initial-scale=1.0'>")
[void]$html.AppendLine("<title>Volsys AD Ultimate Health Check</title>")
[void]$html.AppendLine("<style>")
[void]$html.AppendLine("body { font-family: Segoe UI, Arial, sans-serif; background:#f5f7fb; margin:0; padding:0; color:#1f2937; }")
[void]$html.AppendLine(".header { background:#0f172a; color:white; padding:24px 32px; }")
[void]$html.AppendLine(".header h1 { margin:0; font-size:30px; }")
[void]$html.AppendLine(".header p { margin:6px 0 0 0; opacity:0.9; }")
[void]$html.AppendLine(".container { padding:24px 32px; }")
[void]$html.AppendLine(".cards { display:flex; flex-wrap:wrap; gap:16px; margin-bottom:24px; }")
[void]$html.AppendLine(".card { background:white; border-radius:14px; padding:18px 20px; box-shadow:0 2px 10px rgba(0,0,0,0.08); min-width:180px; }")
[void]$html.AppendLine(".card h3 { margin:0 0 8px 0; font-size:14px; color:#6b7280; text-transform:uppercase; }")
[void]$html.AppendLine(".card .value { font-size:28px; font-weight:700; }")
[void]$html.AppendLine(".section { background:white; border-radius:14px; padding:20px; box-shadow:0 2px 10px rgba(0,0,0,0.08); margin-bottom:24px; }")
[void]$html.AppendLine(".section h2 { margin-top:0; font-size:22px; }")
[void]$html.AppendLine("table { width:100%; border-collapse:collapse; }")
[void]$html.AppendLine("th, td { text-align:left; padding:10px 12px; border-bottom:1px solid #e5e7eb; vertical-align:top; }")
[void]$html.AppendLine("th { background:#f8fafc; }")
[void]$html.AppendLine(".badge { display:inline-block; padding:5px 10px; border-radius:999px; font-size:12px; font-weight:700; }")
[void]$html.AppendLine(".pass { background:#dcfce7; color:#166534; }")
[void]$html.AppendLine(".warn { background:#fef3c7; color:#92400e; }")
[void]$html.AppendLine(".fail { background:#fee2e2; color:#991b1b; }")
[void]$html.AppendLine(".info { background:#dbeafe; color:#1d4ed8; }")
[void]$html.AppendLine(".detail-block { margin-bottom:20px; padding:14px; border:1px solid #e5e7eb; border-radius:12px; background:#fafafa; }")
[void]$html.AppendLine(".detail-block h3 { margin-top:0; }")
[void]$html.AppendLine("ul { margin:0; padding-left:20px; }")
[void]$html.AppendLine("li { margin-bottom:6px; }")
[void]$html.AppendLine("</style>")
[void]$html.AppendLine("</head>")
[void]$html.AppendLine("<body>")
[void]$html.AppendLine("<div class='header'>")
[void]$html.AppendLine("<h1>Volsys AD Ultimate Health Check</h1>")
[void]$html.AppendLine("<p>Generated: $(ConvertTo-HtmlSafe ((Get-Date).ToString()))</p>")
[void]$html.AppendLine("<p>Overall Status: <span class='badge $(Get-StatusClass $overallStatus)'>$(ConvertTo-HtmlSafe $overallStatus)</span></p>")
[void]$html.AppendLine("</div>")
[void]$html.AppendLine("<div class='container'>")
[void]$html.AppendLine("<div class='cards'>")
[void]$html.AppendLine("<div class='card'><h3>Total Checks</h3><div class='value'>$totalCount</div></div>")
[void]$html.AppendLine("<div class='card'><h3>PASS</h3><div class='value'>$passCount</div></div>")
[void]$html.AppendLine("<div class='card'><h3>WARN</h3><div class='value'>$warnCount</div></div>")
[void]$html.AppendLine("<div class='card'><h3>FAIL</h3><div class='value'>$failCount</div></div>")
[void]$html.AppendLine("<div class='card'><h3>INFO</h3><div class='value'>$infoCount</div></div>")
[void]$html.AppendLine("</div>")
[void]$html.AppendLine("<div class='section'>")
[void]$html.AppendLine("<h2>Executive Summary</h2>")
[void]$html.AppendLine("<table>")
[void]$html.AppendLine("<thead><tr><th>Category</th><th>Check</th><th>Status</th><th>Details</th></tr></thead>")
[void]$html.AppendLine("<tbody>")
foreach ($row in $DashboardResults) {
$cls = Get-StatusClass $row.Status
[void]$html.AppendLine("<tr>")
[void]$html.AppendLine("<td>$(ConvertTo-HtmlSafe $row.Category)</td>")
[void]$html.AppendLine("<td>$(ConvertTo-HtmlSafe $row.Check)</td>")
[void]$html.AppendLine("<td><span class='badge $cls'>$(ConvertTo-HtmlSafe $row.Status)</span></td>")
[void]$html.AppendLine("<td>$(ConvertTo-HtmlSafe $row.Details)</td>")
[void]$html.AppendLine("</tr>")
}
[void]$html.AppendLine("</tbody></table>")
[void]$html.AppendLine("</div>")
[void]$html.AppendLine("<div class='section'>")
[void]$html.AppendLine("<h2>Detailed Findings</h2>")
foreach ($section in $DetailSections) {
[void]$html.AppendLine("<div class='detail-block'>")
[void]$html.AppendLine("<h3>$(ConvertTo-HtmlSafe $section.Title)</h3>")
[void]$html.AppendLine("<ul>")
foreach ($line in $section.Lines) {
[void]$html.AppendLine("<li>$(ConvertTo-HtmlSafe $line)</li>")
}
[void]$html.AppendLine("</ul>")
[void]$html.AppendLine("</div>")
}
[void]$html.AppendLine("</div>")
[void]$html.AppendLine("</div>")
[void]$html.AppendLine("</body>")
[void]$html.AppendLine("</html>")
[System.IO.File]::WriteAllText($HtmlReport, $html.ToString(), [System.Text.Encoding]::UTF8)
Write-VolsysLog "HTML dashboard olusturuldu: $HtmlReport"
Write-VolsysLog "TXT rapor olusturuldu : $TxtReport"
Write-VolsysLog ("=" * 100)