Skip to content

Instantly share code, notes, and snippets.

@laymanstake
Last active April 17, 2024 09:53
Show Gist options
  • Save laymanstake/a693a6e9e57563e1571b4cc13d1a1afc to your computer and use it in GitHub Desktop.
Save laymanstake/a693a6e9e57563e1571b4cc13d1a1afc to your computer and use it in GitHub Desktop.
HTML Report for AD Health
# 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 "contactfor_nitish@hotmail.com"
$body = Get-Content $ReportPath -Raw
New-Email -RecipientAddressTo "nitish@nitishkumar.net" -SenderAddress "contactfor_nitish@hotmail.com" -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