# First Clear any variables Remove-Variable * -ErrorAction SilentlyContinue # Start script execution time calculation $ScrptStartTime = (Get-Date).ToString('dd-MM-yyyy hh:mm:ss') $sw = [Diagnostics.Stopwatch]::StartNew() # Get Script Directory $Scriptpath = $($MyInvocation.MyCommand.Path) $Dir = $(Split-Path $Scriptpath); # Report $runntime= (get-date -format dd_MM_yyyy-HH_mm_ss)-as [string] $HealthReport = "$dir\Reports" + "$runntime" + ".htm" # Logfile $Logfile = "$dir\Log" + "$runntime" + ".log" #--------------------------------------------------------------------------------------------------------------------------------------------- # Functions Section #--------------------------------------------------------------------------------------------------------------------------------------------- function Write-Log { [CmdletBinding()] param( [Parameter()] [ValidateNotNullOrEmpty()] [string]$Message, [Parameter()] [ValidateNotNullOrEmpty()] [ValidateSet('Information','Warning','Error')] [string]$Severity = 'Information' ) $LogContent = (Get-Date -f g)+" " + $Severity +" "+$Message Add-Content -Path $logFile -Value $LogContent -PassThru | Write-Host } function Get-IniContent ($filePath) { $ini = @{} switch -regex -file $FilePath { �^\[(.+)\]� # Section { $section = $matches[1] $ini[$section] = @{} $CommentCount = 0 } �^(;.*)$� # Comment { $value = $matches[1] $CommentCount = $CommentCount + 1 $name = �Comment� + $CommentCount $ini[$section][$name] = $value } �(.+?)\s*=(.*)� # Key { $name,$value = $matches[1..2] $ini[$section][$name] = $value } } return $ini } #import AD Module try { Import-Module ActiveDirectory } catch [System.Management.Automation.ParameterBindingException] { Write-Log -Message "Failed Importing Active Directory Module..!" -Severity Error Break; } #Import Configuration Params $params = Get-IniContent -filePath "$dir\Config.ini" # E-mail report details $SendEmail = $params.SMTPSettings.SendEmail.Trim() $emailFrom = $params.SMTPSettings.EmailFrom.Trim() $emailTo = $params.SMTPSettings.EmailTo.Trim() $smtpServer = $params.SMTPSettings.SmtpServer.Trim() $emailSubject = $params.SMTPSettings.EmailSubject.Trim() $DCtoConnect = $params.Config.ConnectorDC.Trim() [string]$date = Get-Date $DCList = @() #--------------------------------------------------------------------------------------------------------------------------- # Setting the header for the Report #--------------------------------------------------------------------------------------------------------------------------- [DateTime]$DisplayDate = ((get-date).ToUniversalTime()) $header = " AD Security Check
$DisplayDate
" Add-Content $HealthReport $header #--------------------------------------------------------------------------------------------------------------------------- # Domain INfo #--------------------------------------------------------------------------------------------------------------------------- try { $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() } catch { Write-Log "Cannot connect to current Domain." Break; } $Domain.DomainControllers | ForEach-Object { $DCList += $_.Name } if(!$DCList) { Write-Log "No Domain Controller found. Run this solution on AD server. Please try again." Break; } Write-Log "List of Domain Controllers Discovered" # List out all machines discovered in Log File and Console foreach ($D in $DCList) { Write-Log "$D" } Add-Content $HealthReport $dataRow # Check if any domain controllers left if($DCList.Count -eq 0) { Write-Log -Message "As no machines left script won't continue further" -Severity Error Break } # Start Container Div and Sub container div $dataRow = "
" $dataRow += "" $forestinfo = Get-ADForest -Server $DCtoConnect $domaininfo = Get-ADDomain -Server $DCtoConnect $dataRow += "" $dataRow += "" $dataRow += "" $dataRow += "" $dataRow += "" $dataRow += "" $dataRow += "" $dataRow += "" $dataRow += "" Add-Content $HealthReport $dataRow Add-Content $HealthReport "

Domain Info

ForestName $($($forestinfo.Name).ToUpper())
DomainName $($($domaininfo.Name).ToUpper())
ForestMode(FFL) $($forestinfo.ForestMode)
DomainMode(DFL) $($domaininfo.DomainMode)
SchemaMaster $($forestinfo.SchemaMaster)
DomainNamingMaster $($forestinfo.DomainNamingMaster)
PDCEmulator $($domaininfo.PDCEmulator)
RIDMaster $($domaininfo.DomainMode)
InfrastructureMaster $($domaininfo.InfrastructureMaster)
" # End Sub Container Div #--------------------------------------------------------------------------- # Domain Users Validation #--------------------------------------------------------------------------- Write-Log -Message "Performing Domain Users Validation..............." # Start Sub Container $DomainUsers= "
" Add-Content $HealthReport $DomainUsers ## Get Domain User Information $LastLoggedOnDate = $(Get-Date) - $(New-TimeSpan -days $params.Config.UserLogonAge) $PasswordStaleDate = $(Get-Date) - $(New-TimeSpan -days $params.Config.UserPasswordAge) $ADLimitedProperties = @("Name","Enabled","SAMAccountname","DisplayName","Enabled","LastLogonDate","PasswordLastSet","PasswordNeverExpires","PasswordNotRequired","PasswordExpired","SmartcardLogonRequired","AccountExpirationDate","AdminCount","Created","Modified","LastBadPasswordAttempt","badpwdcount","mail","CanonicalName","DistinguishedName","ServicePrincipalName","SIDHistory","PrimaryGroupID","UserAccountControl") [array]$DomainUsers = Get-ADUser -Filter * -Property $ADLimitedProperties -Server $DCtoConnect [array]$DomainEnabledUsers = $DomainUsers | Where {$_.Enabled -eq $True } [array]$DomainDisabledUsers = $DomainUsers | Where {$_.Enabled -eq $false } [array]$DomainEnabledInactiveUsers = $DomainEnabledUsers | Where { ($_.LastLogonDate -le $LastLoggedOnDate) -AND ($_.PasswordLastSet -le $PasswordStaleDate) } [array]$DomainUsersWithReversibleEncryptionPasswordArray = $DomainUsers | Where { $_.UserAccountControl -band 0x0080 } [array]$DomainUserPasswordNotRequiredArray = $DomainUsers | Where {$_.PasswordNotRequired -eq $True} [array]$DomainUserPasswordNeverExpiresArray = $DomainUsers | Where {$_.PasswordNeverExpires -eq $True} [array]$DomainKerberosDESUsersArray = $DomainUsers | Where { $_.UserAccountControl -band 0x200000 } [array]$DomainUserDoesNotRequirePreAuthArray = $DomainUsers | Where {$_.DoesNotRequirePreAuth -eq $True} [array]$DomainUsersWithSIDHistoryArray = $DomainUsers | Where {$_.SIDHistory -like "*"} $domainusersrow = "" $domainusersrow += "" $domainusersrow += "" $domainusersrow += "" $domainusersrow += "" $domainusersrow += "" If($($DomainUsersWithReversibleEncryptionPasswordArray.Count) -gt 0){ $temp = @() $DomainUsersWithReversibleEncryptionPasswordArray | ForEach-Object { $temp = $temp + $_.SamAccountName + "
" } $domainusersrow += "" } else{ $domainusersrow += "" } If($($DomainUserPasswordNotRequiredArray.Count) -gt 0){ $temp = @() $DomainUserPasswordNotRequiredArray | ForEach-Object { $temp = $temp + $_.SamAccountName + "
" } $domainusersrow += "" } else{ $domainusersrow += "" } If($($DomainKerberosDESUsersArray.Count) -gt 0){ $temp = @() $DomainKerberosDESUsersArray | ForEach-Object { $temp = $temp + $_.SamAccountName + "
" } $domainusersrow += "" } else{ $domainusersrow += "" } If($($DomainUserDoesNotRequirePreAuthArray.Count) -gt 0){ $temp = @() $DomainUserDoesNotRequirePreAuthArray | ForEach-Object { $temp = $temp + $_.SamAccountName + "
" } $domainusersrow += "" } else{ $domainusersrow += "" } Add-Content $HealthReport $domainusersrow Add-Content $HealthReport "

Domain Users

Total Users $($DomainUsers.Count)
Enabled Users $($DomainEnabledUsers.Count)
Disabled Users $($DomainDisabledUsers.Count)
Inactive Users $($DomainEnabledInactiveUsers.Count)
Users With Password Never Expires $($DomainUserPasswordNeverExpiresArray.Count)
Users With SID History $($DomainUsersWithSIDHistoryArray.Count)
Users With ReversibleEncryptionPasswordArray $($DomainUsersWithReversibleEncryptionPasswordArray.Count)
Users With ReversibleEncryptionPasswordArray $($DomainUsersWithReversibleEncryptionPasswordArray.Count)
Users With Password Not Required $($DomainUserPasswordNotRequiredArray.Count)
Users With Password Not Required $($DomainUserPasswordNotRequiredArray.Count)
Users With Kerberos DES $($DomainKerberosDESUsersArray.Count)
Users With Kerberos DES $($DomainKerberosDESUsersArray.Count)
Users That Do Not Require Kerberos Pre-Authentication $($DomainUserDoesNotRequirePreAuthArray.Count)
Users That Do Not Require Kerberos Pre-Authentication $($DomainUserDoesNotRequirePreAuthArray.Count)
" # End Sub Container Div and Container Div #----------------------- # Domain Password Policy #----------------------- Write-Log -Message "Determining Domain Password Policy........... " #Start Container and Sub Container Div $Pwdpoly = "
" Add-Content $HealthReport $Pwdpoly [array]$DomainPasswordPolicy = Get-ADDefaultDomainPasswordPolicy -Server $DCtoConnect $props = @("ComplexityEnabled","DistinguishedName","LockoutDuration","LockoutObservationWindow","LockoutThreshold","MaxPasswordAge","MinPasswordAge","MinPasswordLength","PasswordHistoryCount","ReversibleEncryptionEnabled") foreach($item in $props){ $flag= 'passed' If(($item -eq 'ComplexityEnabled') -and ($DomainPasswordPolicy.ComplexityEnabled -ne 'True')) { $flag = "failed" } If(($item -eq 'LockoutDuration') -and $DomainPasswordPolicy.LockoutDuration -lt 15) { $flag = "failed" } If(($item -eq 'MaxPasswordAge') -and $DomainPasswordPolicy.MaxPasswordAge -gt 60) { $flag = "failed" } If(($item -eq 'MinPasswordAge') -and $DomainPasswordPolicy.MinPasswordAge -lt 1) { $flag = "failed" } If(($item -eq 'PasswordHistoryCount') -and $DomainPasswordPolicy.PasswordHistoryCount -le '24') { $flag = "failed" } If(($item -eq 'ReversibleEncryptionEnabled') -and $DomainPasswordPolicy.ReversibleEncryptionEnabled -eq 'True') { $flag = "failed" } If(($item -eq 'MinPasswordLength') -and $DomainPasswordPolicy.MinPasswordLength -le 14) { $flag = "failed" } If(($item -eq 'LockoutDuration') -and $DomainPasswordPolicy.LockoutDuration -le 15) { $flag = "failed" } If(($item -eq 'LockoutThreshold') -and ($DomainPasswordPolicy.LockoutThreshold -gt 10 -or $DomainPasswordPolicy.LockoutThreshold -eq 0)) { $flag = "failed" } If(($item -eq 'LockoutObservationWindow') -and $DomainPasswordPolicy.LockoutObservationWindow -le 15) { $flag = "failed" } $Pwdpolyrow += "" } Add-Content $HealthReport $Pwdpolyrow Add-Content $HealthReport "

Domain Password Policy

$item $($DomainPasswordPolicy.$item)
" #End Sub Container #--------------------------------------------------------------------------------------------------------------------------------------------- # Tombstone and Backup Information #--------------------------------------------------------------------------------------------------------------------------------------------- Write-Log -Message "Checking Tombstone and Backup Information........" # Start Sub Container $tsbkp = "
" Add-Content $HealthReport $tsbkp $ADRootDSE = get-adrootdse -Server $DCtoConnect $ADConfigurationNamingContext = $ADRootDSE.configurationNamingContext $TombstoneObjectInfo = Get-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Services,$ADConfigurationNamingContext" ` -Partition "$ADConfigurationNamingContext" -Properties * [int]$TombstoneLifetime = $TombstoneObjectInfo.tombstoneLifetime IF ($TombstoneLifetime -eq 0) { $TombstoneLifetime = 60 } $tsbkprow += "" [string[]]$Partitions = (Get-ADRootDSE -Server $DCtoConnect).namingContexts $contextType = [System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain $context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext($contextType,$($domaininfo.DNSRoot)) $domainController = [System.DirectoryServices.ActiveDirectory.DomainController]::findOne($context) ForEach($partition in $partitions) { $domainControllerMetadata = $domainController.GetReplicationMetadata($partition) $dsaSignature = $domainControllerMetadata.Item(�dsaSignature�) Write-Log �$partition was backed up $($dsaSignature.LastOriginatingChangeTime.DateTime)" $tsbkprow += "" } Add-Content $HealthReport $tsbkprow Add-Content $HealthReport "

Tombstone & Partitions Backup

TombstoneLifetime $TombstoneLifetime
Last backup of '$partition' $($dsaSignature.LastOriginatingChangeTime.ToShortDateString())
" # End Sub Container and Container Div #--------------------------------------------------------------------------------------------------------------------------------------------- # Kerberos delegation Info #--------------------------------------------------------------------------------------------------------------------------------------------- Write-Log -Message "Checking Kerberos delegation Info........" # Start Sub Container $krbtgtdel = "
" Add-Content $HealthReport $krbtgtdel ## Identify Accounts with Kerberos Delegation $KerberosDelegationArray = @() [array]$KerberosDelegationObjects = Get-ADObject -filter { (UserAccountControl -BAND 0x0080000) -AND (PrimaryGroupID -ne '516') -AND (PrimaryGroupID -ne '521') } -Server $DCtoConnect -prop Name,ObjectClass,PrimaryGroupID,UserAccountControl,ServicePrincipalName ForEach ($KerberosDelegationObjectItem in $KerberosDelegationObjects) { IF ($KerberosDelegationObjectItem.UserAccountControl -BAND 0x0080000) { $KerberosDelegationServices = 'All Services' ; $KerberosType = 'Unconstrained' } ELSE { $KerberosDelegationServices = 'Specific Services' ; $KerberosType = 'Constrained' } $KerberosDelegationObjectItem | Add-Member -MemberType NoteProperty -Name KerberosDelegationServices -Value $KerberosDelegationServices -Force [array]$KerberosDelegationArray += $KerberosDelegationObjectItem } $Requiredpros = $KerberosDelegationArray | Select Name,ObjectClass $Groupedresult = $Requiredpros | Group ObjectClass -AsHashTable $Groupedresult.Keys | ForEach-Object { $objs = "" $($Groupedresult.$PSItem.Name) | foreach { $objs = $objs + $_ + "
" } $krbtgtdelrow += "" } Add-Content $HealthReport $krbtgtdelrow Add-Content $HealthReport "

Kerberos Delegation (Unconstrained)

ObjectClass Count
$($PSItem) $($Groupedresult.$PSItem.Name.count)
" # End Sub Container #--------------------------------------------------------------------------------------------------------------------------------------------- # Scan SYSVOL for Group Policy Preference Passwords #--------------------------------------------------------------------------------------------------------------------------------------------- Write-Log -Message "Scan SYSVOL for Group Policy Preference Passwords......." # Start Sub Container $gpppwd = "
" Add-Content $HealthReport $gpppwd $domainname = ($domaininfo.DistinguishedName.Replace("DC=","")).replace(",",".") $DomainSYSVOLShareScan = "\\$domainname\SYSVOL\$domainname\Policies\" [int]$Count = 0 $Passfoundfiles = "" $flag = "passed" Get-ChildItem $DomainSYSVOLShareScan -Filter *.xml -Recurse | % { If(Select-String -Path $_.FullName -Pattern "Cpassword"){ $Passfoundfiles += $_.FullName + "
" ; $Count += 1; $flag= "failed" } } $gpppwdrow += "" Add-Content $HealthReport $gpppwdrow Add-Content $HealthReport "

Scan SYSVOL for Group Policy Preference Passwords

Items Found $Count
" # End Sub Container and Container Div #------------------------------------- # KRBTGT account info #------------------------------------- Write-Log -Message "Checking KRBTGT account info........" $krbtgt = "
" Add-Content $HealthReport $krbtgt $DomainKRBTGTAccount = Get-ADUser 'krbtgt' -Server $DCtoConnect -Properties 'msds-keyversionnumber',Created,PasswordLastSet If($(New-TimeSpan -Start ($DomainKRBTGTAccount.PasswordLastSet) -End $(Get-Date)).Days -gt 180) { $flag = "failed" } else { $flag = "passed" } $SelectedPros = @("DistinguishedName","Enabled","msds-keyversionnumber","PasswordLastSet","Created") $SelectedPros | % { $krbtgtrow += " " } Add-Content $HealthReport $krbtgtrow Add-Content $HealthReport "

KRBTGT Account Info

DistinguishedName Enabled msds-keyversionnumber PasswordLastSet Created
$($DomainKRBTGTAccount.$PSItem)
" #----------------------- # Privileged AD Group Report #----------------------- Write-Log -Message "Performing Privileged AD Group Report......." #Start Container and Sub Container Div $group = "
" Add-Content $HealthReport $group $ADPrivGroupArray = @( 'Administrators', 'Domain Admins', 'Enterprise Admins', 'Schema Admins', 'Account Operators', 'Server Operators', 'Group Policy Creator Owners', 'DNSAdmins', 'Enterprise Key Admins', 'Exchange Domain Servers', 'Exchange Enterprise Servers', 'Exchange Admins', 'Organization Management', 'Exchange Windows Permissions' ) foreach($group in $ADPrivGroupArray){ try { $GrpProps = Get-ADGroupMember -Identity $group -Recursive -Server $DCtoConnect -ErrorAction SilentlyContinue | select SamAccountName,distinguishedName $tempobj = "" $GrpProps | % { $tempobj = $tempobj + $_.SamAccountName +"(" + $_.distinguishedName + ")" + "
" } $grouprow += "" } catch{ $grouprow += "" } } Add-Content $HealthReport $grouprow Add-Content $HealthReport "

Privileged AD Group Info

Privileged Group Name Members Count
$group $($GrpProps.SamAccountName.count)
$group NA
" #End Container #--------------------------------------------------------------------------------------------------------------------------------------------- # Script Execution Time #--------------------------------------------------------------------------------------------------------------------------------------------- $myhost = $env:COMPUTERNAME $ScriptExecutionRow = "
" # Stop script execution time calculation $sw.Stop() $Days = $sw.Elapsed.Days $Hours = $sw.Elapsed.Hours $Minutes = $sw.Elapsed.Minutes $Seconds = $sw.Elapsed.Seconds $Milliseconds = $sw.Elapsed.Milliseconds $ScriptStopTime = (Get-Date).ToString('dd-MM-yyyy hh:mm:ss') $Elapsed = " " $ScriptExecutionRow += $Elapsed Add-Content $HealthReport $ScriptExecutionRow Add-Content $HealthReport "

Execution Details

Start Time Stop Time Days Hours Minutes Seconds Milliseconds Script Executed on
$ScrptStartTime $ScriptStopTime $Days $Hours $Minutes $Seconds $Milliseconds $myhost
" #--------------------------------------------------------------------------------------------------------------------------------------------- # Sending Mail #--------------------------------------------------------------------------------------------------------------------------------------------- if($SendEmail -eq 'Yes' ) { # Send ADHealthCheck Report if(Test-Path $HealthReport) { try { $body = "Please find AD Health Check report attached." #$port = "25" Send-MailMessage -Priority High -Attachments $HealthReport -To $emailTo -From $emailFrom -SmtpServer $smtpServer -Body $Body -Subject $emailSubject -Credential $Credentials -UseSsl -Port 587 -ErrorAction Stop } catch { Write-Log 'Error in sending AD Health Check Report' } } #Send an ERROR mail if Report is not found if(!(Test-Path $HealthReport)) { try { $body = "ERROR: NO AD Health Check report" $port = "25" Send-MailMessage -Priority High -To $emailTo -From $emailFrom -SmtpServer $smtpServer -Body $Body -Subject $emailSubject -Port $port -ErrorAction Stop } catch { Write-Log 'Unable to send Error mail.' } } } else { Write-Log "As Send Email is NO so report through mail is not being sent. Please find the report in Script directory." }