Last active
April 17, 2024 09:53
-
-
Save laymanstake/a693a6e9e57563e1571b4cc13d1a1afc to your computer and use it in GitHub Desktop.
HTML Report for AD Health
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Output formating options | |
$logopath = "https://camo.githubusercontent.com/239d9de795c471d44ad89783ec7dc03a76f5c0d60d00e457c181b6e95c6950b6/68747470733a2f2f6e69746973686b756d61722e66696c65732e776f726470726573732e636f6d2f323032322f31302f63726f707065642d696d675f32303232303732335f3039343534372d72656d6f766562672d707265766965772e706e67" | |
$ReportPath = "$env:USERPROFILE\desktop\ADReport_$(get-date -Uformat "%Y%m%d-%H%M%S").html" | |
$CopyRightInfo = " @Copyright Nitish Kumar <a href='https://github.com/laymanstake'>Visit nitishkumar.net</a>" | |
# CSS codes to format the report | |
$header = @" | |
<style> | |
body { background-color: #b9d7f7; } | |
h1 { font-family: Arial, Helvetica, sans-serif; color: #e68a00; font-size: 28px; } | |
h2 { font-family: Arial, Helvetica, sans-serif; color: #000099; font-size: 16px; } | |
table { font-size: 12px; border: 1px; font-family: Arial, Helvetica, sans-serif; } | |
td { padding: 4px; margin: 0px; border: 1; } | |
th { background: #395870; background: linear-gradient(#49708f, #293f50); color: #fff; font-size: 11px; text-transform: uppercase; padding: 10px 15px; vertical-align: middle; } | |
tbody tr:nth-child(even) { background: #f0f0f2; } | |
CreationDate { font-family: Arial, Helvetica, sans-serif; color: #ff3300; font-size: 12px; } | |
</style> | |
"@ | |
If ($logopath) { | |
$header = $header + "<img src=$logopath alt='Company logo' width='150' height='150' align='right'>" | |
} | |
# This function generates an email message. | |
function New-Email { | |
[CmdletBinding()] | |
param( | |
[parameter(mandatory = $true)]$RecipientAddressTo, | |
[parameter(mandatory = $true)]$SenderAddress, | |
[parameter(mandatory = $true)]$SMTPServer, | |
[parameter(mandatory = $true)]$Subject, | |
[parameter(mandatory = $true)]$Body, | |
[parameter(mandatory = $false)]$SMTPServerPort = "25", | |
[parameter(mandatory = $false)]$RecipientAddressCc, | |
[parameter(mandatory = $true)][pscredential]$Credential | |
) | |
if ($RecipientAddressCc) { | |
try { | |
$email = @{ | |
From = $SenderAddress | |
To = $RecipientAddressTo | |
Cc = $RecipientAddressCc | |
Subject = $Subject | |
Body = $Body | |
SmtpServer = $SMTPServer | |
Port = $SMTPServerPort | |
} | |
Send-MailMessage @email -UseSsl -BodyAsHtml -Credential $credential | |
} | |
Catch { | |
Throw $_.exception.message | |
} | |
} | |
else { | |
try { | |
$email = @{ | |
From = $SenderAddress | |
To = $RecipientAddressTo | |
Subject = $Subject | |
Body = $Body | |
SmtpServer = $SMTPServer | |
Port = $SMTPServerPort | |
} | |
Send-MailMessage @email -UseSsl -BodyAsHtml -Credential $credential | |
} | |
Catch { | |
Throw $_.exception.message | |
} | |
} | |
} | |
function Get-ADReplicationHealth { | |
[CmdletBinding()] | |
Param( | |
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$DomainName | |
) | |
$replicationData = @() | |
$domainControllers = Get-ADDomainController -Filter * -Server $DomainName | |
foreach ($dc in $domainControllers) { | |
try { | |
$dcName = $dc.Name | |
$replicationInfo = Get-ADReplicationPartnerMetadata -Target $dcName -ErrorAction SilentlyContinue | |
$replicationFailures = Get-ADReplicationFailure -Target $dcName -ErrorAction SilentlyContinue | |
foreach ($partner in $replicationInfo) { | |
$partnerData = Get-ADDomainController -Identity $partner.Partner -Server $DomainName | |
$replicationStatus = $partner.LastReplicationResult | |
$lastReplicationTime = $partner.LastReplicationSuccess | |
$LastReplicationAttempt = $partner.LastReplicationAttempt | |
$failure = $replicationFailures | Where-Object { $_.Partner -eq $partner.Partner } | |
$replicationData += [PSCustomObject] @{ | |
DomainController = $dcName | |
Partner = $partnerData.Name | |
ReplicationStatus = $replicationStatus | |
LastReplicationSuccessTime = $lastReplicationTime | |
LastReplicationTimeAttempt = $LastReplicationAttempt | |
FirstFailureTime = $failure.FirstFailureTime -join "`n" | |
FailureCount = $failure.FailureCount -join "`n" | |
FailureType = $failure.FailureType -join "`n" | |
FailureError = $failure.LastError -join "`n" | |
} | |
} | |
} | |
catch { | |
Write-Output "Could not check $($dc.Name) for replication health : $($_.exception.message)" | |
} | |
} | |
return $replicationData | |
} | |
# This function performs a health check of the Active Directory environment, including checks for replication, DNS, AD trust, and other common issues. | |
function Test-ADHealth { | |
[CmdletBinding()] | |
Param( | |
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$DomainName | |
) | |
$Report = @() | |
$dcs = Get-ADDomainController -Filter * -Server $DomainName | |
$jobs = foreach ($Dcserver in $dcs) { | |
$Job = Start-Job -ScriptBlock { | |
param($DC) | |
$Result = [pscustomobject] @{ | |
DCName = $DC.Hostname | |
DCSIteName = $DC.Site -join "," | |
DCFSMORoles = $DC.OperationMasterRoles -join "," | |
OperatingSystem = $null | |
Uptime = $null | |
DeviceID = $null | |
TotalSpace = $null | |
Freespace = $null | |
Freespace_Percent = $null | |
TotalMemory = $null | |
FreeMemory = $null | |
FreeMemory_percent = $null | |
Ping = $null | |
Netlogon = $null | |
NTDS = $null | |
DNS = $null | |
DCDIAG_Netlogons = $null | |
DCDIAG_Services = $null | |
DCDIAG_Replications = $null | |
DCDIAG_FSMOCheck = $null | |
DCDIAG_Advertising = $null | |
DCDIAG_SYSVOL = $null | |
} | |
if (Test-Connection -ComputerName $DC.Hostname -Count 2 -Quiet) { | |
$Result.Ping = "OK" | |
$output = Get-Service -Name DNS, NTDS, Netlogon -ComputerName $DC.Hostname -ErrorAction SilentlyContinue | Select-Object Name, Status | |
# Netlogon Service Status | |
$netlogonstatus = ($output | Where-Object { $_.Name -eq "Netlogon" } | Select-object Status).Status | |
if ($netlogonstatus -eq "Running") { | |
$Result.Netlogon = "OK" | |
} | |
else { | |
$Result.Netlogon = $netlogonstatus | |
} | |
# NTDS Service Status | |
$NTDSstatus = ($output | Where-Object { $_.Name -eq "NTDS" } | Select-object Status).Status | |
if ($NTDSstatus -eq "Running") { | |
$Result.NTDS = "OK" | |
} | |
else { | |
$Result.NTDS = $NTDSstatus | |
} | |
# DNS Service Status | |
$DNSstatus = ($output | Where-Object { $_.Name -eq "DNS" } | Select-object Status).Status | |
if ($DNSstatus -eq "Running") { | |
$Result.DNS = "OK" | |
} | |
else { | |
$Result.DNS = $DNSstatus | |
} | |
# Dcdiag netlogons "Checking now" | |
$dcdiagnetlogon = dcdiag /test:netlogons /s:$($DC.Hostname) | |
if ($dcdiagnetlogon -match "passed test NetLogons") { | |
$Result.DCDIAG_Netlogons = "OK" | |
} | |
else { | |
$Result.DCDIAG_Netlogons = (($dcdiagnetlogon | Select-String "Error", "warning" | ForEach-Object { $_.Line.Trim() }) -join "`n") + "`n`nRun dcdiag /test:netlogons /s:$($DC.Hostname)" | |
} | |
# Dcdiag services check | |
$dcdiagservices = dcdiag /test:services /s:$($DC.Hostname) | |
if ($dcdiagservices -match "passed test services") { | |
$Result.DCDIAG_Services = "OK" | |
} | |
else { | |
$Result.DCDIAG_Services = (($dcdiagservices | Select-String "Error", "warning" | ForEach-Object { $_.Line.Trim() }) -join "`n") + "`n`nRun dcdiag /test:services /s:$($DC.Hostname)" | |
} | |
# Dcdiag Replication Check | |
$dcdiagreplications = dcdiag /test:Replications /s:$($DC.Hostname) | |
if ($dcdiagreplications -match "passed test Replications") { | |
$Result.DCDIAG_Replications = "OK" | |
} | |
else { | |
$Result.DCDIAG_Replications = (($dcdiagreplications | Select-String "Error", "warning" | ForEach-Object { $_.Line.Trim() }) -join "`n") + "`n`nRun dcdiag /test:Replications /s:$($DC.Hostname)" | |
} | |
# Dcdiag FSMOCheck Check | |
$dcdiagFsmoCheck = dcdiag /test:FSMOCheck /s:$($DC.Hostname) | |
if ($dcdiagFsmoCheck -match "passed test FsmoCheck") { | |
$Result.DCDIAG_FSMOCheck = "OK" | |
} | |
else { | |
$Result.DCDIAG_FSMOCheck = (($dcdiagFsmoCheck | Select-String "Error", "warning" | ForEach-Object { $_.Line.Trim() }) -join "`n") + "`n`nRun dcdiag /test:FSMOCheck /s:$($DC.Hostname)" | |
} | |
# Dcdiag Advertising Check | |
$dcdiagAdvertising = dcdiag /test:Advertising /s:$($DC.Hostname) | |
if ($dcdiagAdvertising -match "passed test Advertising") { | |
$Result.DCDIAG_Advertising = "OK" | |
} | |
else { | |
$Result.DCDIAG_Advertising = (($dcdiagAdvertising | Select-String "Error", "warning" | ForEach-Object { $_.Line.Trim() }) -join "`n") + "`n`nRun dcdiag /test:Advertising /s:$($DC.Hostname)" | |
} | |
# Dcdiag SYSVOL Check | |
$dcdiagSYSVOL = dcdiag /test:SYSVOLCHECK /s:$($DC.Hostname) | |
if ($dcdiagSYSVOL -match "passed test SysVolCheck") { | |
$Result.DCDIAG_SYSVOL = "OK" | |
} | |
else { | |
$Result.DCDIAG_SYSVOL = (($dcdiagSYSVOL | Select-String "Error", "warning" | ForEach-Object { $_.Line.Trim() }) -join "`n") + "`n`nRun dcdiag /test:SYSVOLCHECK /s:$($DC.Hostname)" | |
} | |
$DiskInfo = Get-CimInstance -ClassName Win32_logicaldisk -ComputerName $DC.Hostname | Where-Object { $_.DeviceID -ne "A:" -AND $_.DriveType -eq 3 } | Select-Object @{l = "DCName"; e = { $_.SystemName } }, DeviceID, @{l = "TotalSize"; e = { "{0:N2}" -f ($_.Size / 1GB) } }, @{l = "FreeSize"; e = { "{0:N2}" -f ($_.Freespace / 1GB) } }, @{l = "Freespace_Percent"; e = { "{0:N2}%" -f (($_.Freespace / $_.Size) * 100) } } | |
$FreeMemory = (Get-CimInstance Win32_PerfFormattedData_PerfOS_Memory -ComputerName $DC.Hostname).AvailableMBytes | |
$TotalMemory = "{0:N2}" -f ((Get-CimInstance Win32_ComputerSystem -ComputerName $DC.Hostname).TotalPhysicalMemory / 1Mb) | |
$result.DeviceID = $DiskInfo.DeviceID | |
$result.TotalSpace = $DiskInfo.TotalSize | |
$result.Freespace = $DiskInfo.FreeSize | |
$result.Freespace_Percent = $DiskInfo.Freespace_Percent | |
$result.TotalMemory = $TotalMemory | |
$result.FreeMemory = $FreeMemory | |
$result.FreeMemory_percent = "{0:N2}%" -f ($FreeMemory / $TotalMemory) | |
$Result.Uptime = "{0:N2}" -f ((Get-Date) - (Get-CimInstance Win32_OperatingSystem -ComputerName $DC.Hostname).LastBootUpTime).TotalHours | |
try { | |
$result.OperatingSystem = (Get-WmiObject Win32_OperatingSystem -ComputerName $DC.Hostname).Caption | |
} | |
catch { | |
$result.OperatingSystem = "DC access denied" | |
} | |
} | |
else { | |
$result.OperatingSystem = "DC is down" | |
$Result.Uptime = "DC is down" | |
$result.DeviceID = "DC is down" | |
$result.TotalSpace = "DC is down" | |
$result.Freespace = "DC is down" | |
$result.Freespace_Percent = "DC is down" | |
$result.TotalMemory = "DC is down" | |
$result.FreeMemory = "DC is down" | |
$result.FreeMemory_percent = "DC is down" | |
$Result.Ping = "DC is down" | |
$Result.Netlogon = "DC is down" | |
$Result.NTDS = "DC is down" | |
$Result.DNS = "DC is down" | |
$Result.DCDIAG_Netlogons = "DC is down" | |
$Result.DCDIAG_Services = "DC is down" | |
$Result.DCDIAG_Replications = "DC is down" | |
$Result.DCDIAG_FSMOCheck = "DC is down" | |
$Result.DCDIAG_Advertising = "DC is down" | |
$Result.DCDIAG_SYSVOL = "DC is down" | |
} | |
$Result | Select-Object DCName, DCSIteName, OperatingSystem, DCFSMORoles, Uptime, DeviceID, TotalSpace, Freespace, Freespace_Percent, TotalMemory, FreeMemory, FreeMemory_percent, Ping, NTDS, Netlogon, DNS, DCDIAG_Netlogons, DCDIAG_Services, DCDIAG_FSMOCheck, DCDIAG_Replications, DCDIAG_Advertising, DCDIAG_SYSVOL | |
} -ArgumentList $Dcserver | |
$Job | |
} | |
$null = Wait-Job -Job $jobs | |
$Report = foreach ($Job in $jobs) { | |
Receive-Job -Job $Job | |
} | |
Remove-Job -Job $jobs | |
$Report = $Report | Select-Object DCName, DCSIteName, OperatingSystem, DCFSMORoles, @{l = "Uptime (hours)"; e = { $_.uptime } }, DeviceID, TotalSpace, Freespace, Freespace_Percent, TotalMemory, FreeMemory, FreeMemory_percent, Ping, NTDS, Netlogon, DNS, DCDIAG_Netlogons, DCDIAG_Services, DCDIAG_FSMOCheck, DCDIAG_Replications, DCDIAG_Advertising, DCDIAG_SYSVOL | |
return $Report | |
} | |
$DomainName = "ADLAB.LOCAL" | |
$ADHealthData = Test-ADHealth -DomainName $DomainName | |
$HealthSummary = ( $ADHealthData | Select-Object DCName, DCSIteName, OperatingSystem, DCFSMORoles, Uptime, Ping, NTDS, Netlogon, DNS, DCDIAG_Netlogons, DCDIAG_Services, DCDIAG_FSMOCheck, DCDIAG_Replications, DCDIAG_Advertising, DCDIAG_SYSVOL | ConvertTo-Html -As Table -Fragment -PreContent "<h2>AD health summary</h2>") -replace "`n", "<br>" | |
$HWHealthSummary = ( $ADHealthData | Select-Object DCName, DeviceID, TotalSpace, Freespace, Freespace_Percent, TotalMemory, FreeMemory, FreeMemory_percent | ConvertTo-Html -As Table -Fragment -PreContent "<h2>Domain Controller Hardware health summary</h2>") -replace "`n", "<br>" | |
$ReplicationSummary = (Get-ADReplicationHealth -DomainName $DomainName | ConvertTo-Html -As Table -Fragment -PreContent "<h2>AD Replication health summary</h2>") -replace "`n", "<br>" | |
$ReportRaw = ConvertTo-HTML -Body "$HealthSummary $ReplicationSummary $HWHealthSummary" -Head $header -Title "Report on AD Domain: $DomainName" -PostContent "<p id='CreationDate'>Creation Date: $(Get-Date) $CopyRightInfo </p>" | |
$ReportRaw | Out-File $ReportPath | |
<# $MailCredential = Get-Credential -Message "Enter the password for the email account: " -UserName "[email protected]" | |
$body = Get-Content $ReportPath -Raw | |
New-Email -RecipientAddressTo "[email protected]" -SenderAddress "[email protected]" -SMTPServer "smtp.office365.com" -SMTPServerPort 587 -Subject "AD Assessment Report $(get-date -Uformat "%Y%m%d-%H%M%S")" -Body $body -credential $MailCredential #> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment