a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
656 lines
23 KiB
PowerShell
656 lines
23 KiB
PowerShell
###############################################################################
|
|
#### windows-ad-smoke-tests.ps1 — Verify Active Directory health ####
|
|
#### Checks DC connectivity, LDAP, DNS, replication, FSMO, shares, ####
|
|
#### Kerberos, Group Policy, ADWS, trusts, and time sync. ####
|
|
#### ####
|
|
#### Author: Phil Connor ####
|
|
#### Contact: contact@mylinux.work ####
|
|
#### License: MIT ####
|
|
#### Version: 1.0 ####
|
|
#### ####
|
|
#### Usage: .\windows-ad-smoke-tests.ps1 ####
|
|
#### .\windows-ad-smoke-tests.ps1 -DomainController DC01 ####
|
|
#### .\windows-ad-smoke-tests.ps1 -OutputFormat tap ####
|
|
#### ####
|
|
#### See -Help for all options. ####
|
|
###############################################################################
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$DomainController = "",
|
|
[string]$Domain = "",
|
|
[ValidateSet("text","tap")]
|
|
[string]$OutputFormat = "text",
|
|
[switch]$NoColor,
|
|
[switch]$Help
|
|
)
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
# ============================================================================
|
|
# HELP
|
|
# ============================================================================
|
|
|
|
if ($Help) {
|
|
@"
|
|
Usage: .\windows-ad-smoke-tests.ps1 [OPTIONS]
|
|
|
|
Smoke-test Active Directory infrastructure. PowerShell 5.1+.
|
|
Designed for domain-joined Windows machines.
|
|
|
|
Parameters:
|
|
-DomainController DC Target DC hostname or IP (default: auto-detect via nltest)
|
|
-Domain FQDN AD domain FQDN (default: auto-detect via USERDNSDOMAIN)
|
|
-OutputFormat FORMAT Output: text (default), tap
|
|
-NoColor Disable coloured output
|
|
-Verbose Show debug output
|
|
-Help Show this help
|
|
|
|
Examples:
|
|
.\windows-ad-smoke-tests.ps1
|
|
.\windows-ad-smoke-tests.ps1 -DomainController DC01.corp.local
|
|
.\windows-ad-smoke-tests.ps1 -Domain corp.local -OutputFormat tap
|
|
.\windows-ad-smoke-tests.ps1 -NoColor -Verbose
|
|
"@
|
|
exit 0
|
|
}
|
|
|
|
# ============================================================================
|
|
# STATE
|
|
# ============================================================================
|
|
|
|
$script:Pass = 0
|
|
$script:Fail = 0
|
|
$script:Skip = 0
|
|
$script:Total = 0
|
|
$script:Results = @()
|
|
$script:StartTime = Get-Date
|
|
|
|
# ============================================================================
|
|
# COLORS
|
|
# ============================================================================
|
|
|
|
function Write-Color {
|
|
param([string]$Text, [string]$Color = "White")
|
|
if ($NoColor) {
|
|
Write-Host $Text
|
|
} else {
|
|
Write-Host $Text -ForegroundColor $Color
|
|
}
|
|
}
|
|
|
|
function Write-Log { param([string]$Msg) Write-Color "[INFO] $Msg" "Cyan" }
|
|
function Write-Warn { param([string]$Msg) Write-Color "[WARN] $Msg" "Yellow" }
|
|
function Write-Err { param([string]$Msg) Write-Color "[ERROR] $Msg" "Red" }
|
|
|
|
# ============================================================================
|
|
# TEST RESULT RECORDING
|
|
# ============================================================================
|
|
|
|
function Record-Pass {
|
|
param([string]$Name, [string]$Detail = "")
|
|
$script:Pass++
|
|
$script:Total++
|
|
$script:Results += [PSCustomObject]@{ Status="PASS"; Name=$Name; Detail=$Detail }
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "ok $($script:Total) - $Name$(if($Detail){" ($Detail)"})"
|
|
} else {
|
|
$mark = if ($NoColor) { "[PASS]" } else { [char]0x2713 }
|
|
$msg = " $mark $Name"
|
|
if ($Detail) { $msg += " - $Detail" }
|
|
Write-Color $msg "Green"
|
|
}
|
|
}
|
|
|
|
function Record-Fail {
|
|
param([string]$Name, [string]$Detail = "")
|
|
$script:Fail++
|
|
$script:Total++
|
|
$script:Results += [PSCustomObject]@{ Status="FAIL"; Name=$Name; Detail=$Detail }
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "not ok $($script:Total) - $Name"
|
|
if ($Detail) { Write-Host " # $Detail" }
|
|
} else {
|
|
$mark = if ($NoColor) { "[FAIL]" } else { [char]0x2717 }
|
|
$msg = " $mark $Name"
|
|
if ($Detail) { $msg += " - $Detail" }
|
|
Write-Color $msg "Red"
|
|
}
|
|
}
|
|
|
|
function Record-Skip {
|
|
param([string]$Name, [string]$Reason = "")
|
|
$script:Skip++
|
|
$script:Total++
|
|
$script:Results += [PSCustomObject]@{ Status="SKIP"; Name=$Name; Detail=$Reason }
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "ok $($script:Total) - $Name # SKIP $Reason"
|
|
} else {
|
|
$mark = if ($NoColor) { "[SKIP]" } else { [char]0x2298 }
|
|
$msg = " $mark $Name"
|
|
if ($Reason) { $msg += " - $Reason" }
|
|
Write-Color $msg "Yellow"
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# HELPERS
|
|
# ============================================================================
|
|
|
|
function Test-CommandExists {
|
|
param([string]$Command)
|
|
$null -ne (Get-Command $Command -ErrorAction SilentlyContinue)
|
|
}
|
|
|
|
function Write-Section {
|
|
param([string]$Name)
|
|
if ($OutputFormat -eq "text") {
|
|
Write-Host ""
|
|
Write-Color $Name "White"
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# AUTO-DETECTION
|
|
# ============================================================================
|
|
|
|
function Resolve-DomainController {
|
|
if ($DomainController) {
|
|
Write-Verbose "Using specified DC: $DomainController"
|
|
return $DomainController
|
|
}
|
|
|
|
try {
|
|
$nltest = nltest /dsgetdc: 2>&1
|
|
$dcLine = $nltest | Where-Object { $_ -match "DC: \\\\" }
|
|
if ($dcLine -match "DC: \\\\(.+)$") {
|
|
$detected = $Matches[1].Trim()
|
|
Write-Verbose "Auto-detected DC: $detected"
|
|
return $detected
|
|
}
|
|
} catch {}
|
|
|
|
try {
|
|
$dcLocator = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
|
|
$dc = $dcLocator.FindDomainController()
|
|
if ($dc.Name) {
|
|
Write-Verbose "Auto-detected DC via .NET: $($dc.Name)"
|
|
return $dc.Name
|
|
}
|
|
} catch {}
|
|
|
|
Write-Err "Cannot auto-detect domain controller. Use -DomainController parameter."
|
|
exit 1
|
|
}
|
|
|
|
function Resolve-Domain {
|
|
if ($Domain) {
|
|
Write-Verbose "Using specified domain: $Domain"
|
|
return $Domain
|
|
}
|
|
|
|
if ($env:USERDNSDOMAIN) {
|
|
Write-Verbose "Auto-detected domain: $($env:USERDNSDOMAIN)"
|
|
return $env:USERDNSDOMAIN
|
|
}
|
|
|
|
try {
|
|
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
|
|
if ($dom.Name) {
|
|
Write-Verbose "Auto-detected domain via .NET: $($dom.Name)"
|
|
return $dom.Name
|
|
}
|
|
} catch {}
|
|
|
|
Write-Err "Cannot auto-detect domain. Use -Domain parameter."
|
|
exit 1
|
|
}
|
|
|
|
# ============================================================================
|
|
# TESTS
|
|
# ============================================================================
|
|
|
|
# -- 1. DC Connectivity ----------------------------------------------------
|
|
|
|
function Test-DCConnectivity {
|
|
Write-Section "Connectivity"
|
|
|
|
try {
|
|
$ping = Test-Connection -ComputerName $script:DC -Count 2 -Quiet -ErrorAction Stop
|
|
if ($ping) {
|
|
Record-Pass "DC connectivity" $script:DC
|
|
} else {
|
|
Record-Fail "DC connectivity" "$($script:DC) - no response"
|
|
}
|
|
} catch {
|
|
Record-Fail "DC connectivity" "$($script:DC) - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 2. LDAP Bind -----------------------------------------------------------
|
|
|
|
function Test-LDAPBind {
|
|
Write-Section "LDAP"
|
|
|
|
try {
|
|
$ldapPath = "LDAP://$($script:DC)"
|
|
$entry = New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
|
|
$searcher = New-Object System.DirectoryServices.DirectorySearcher($entry)
|
|
$searcher.SearchScope = "Base"
|
|
$searcher.PropertiesToLoad.Add("defaultNamingContext") | Out-Null
|
|
$result = $searcher.FindOne()
|
|
|
|
if ($result) {
|
|
$nc = $result.Properties["defaultnamingcontext"][0]
|
|
Record-Pass "LDAP bind" "$($script:DC) - $nc"
|
|
} else {
|
|
Record-Fail "LDAP bind" "$($script:DC) - bind succeeded but no result"
|
|
}
|
|
|
|
$searcher.Dispose()
|
|
$entry.Dispose()
|
|
} catch {
|
|
Record-Fail "LDAP bind" "$($script:DC) - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 3. LDAPS Connectivity --------------------------------------------------
|
|
|
|
function Test-LDAPS {
|
|
try {
|
|
$tcp = Test-NetConnection -ComputerName $script:DC -Port 636 -WarningAction SilentlyContinue -ErrorAction Stop
|
|
if ($tcp.TcpTestSucceeded) {
|
|
Record-Pass "LDAPS connectivity" "$($script:DC):636"
|
|
} else {
|
|
Record-Fail "LDAPS connectivity" "$($script:DC):636 - port closed"
|
|
}
|
|
} catch {
|
|
if (Test-CommandExists "Test-NetConnection") {
|
|
Record-Fail "LDAPS connectivity" "$($script:DC):636 - $($_.Exception.Message)"
|
|
} else {
|
|
Record-Skip "LDAPS connectivity" "Test-NetConnection not available"
|
|
}
|
|
}
|
|
}
|
|
|
|
# -- 4. DNS SRV Records ----------------------------------------------------
|
|
|
|
function Test-DNSSrvRecords {
|
|
Write-Section "DNS"
|
|
|
|
$srvRecord = "_ldap._tcp.dc._msdcs.$($script:DomainFQDN)"
|
|
|
|
try {
|
|
if (Test-CommandExists "Resolve-DnsName") {
|
|
$results = Resolve-DnsName -Name $srvRecord -Type SRV -ErrorAction Stop
|
|
if ($results) {
|
|
$count = ($results | Where-Object { $_.QueryType -eq "SRV" }).Count
|
|
Record-Pass "DNS SRV records" "$srvRecord - $count record(s)"
|
|
} else {
|
|
Record-Fail "DNS SRV records" "$srvRecord - no records returned"
|
|
}
|
|
} else {
|
|
$nslookup = nslookup -type=srv $srvRecord 2>&1
|
|
if ($nslookup -match "svr hostname") {
|
|
Record-Pass "DNS SRV records" $srvRecord
|
|
} else {
|
|
Record-Fail "DNS SRV records" "$srvRecord - lookup failed"
|
|
}
|
|
}
|
|
} catch {
|
|
Record-Fail "DNS SRV records" "$srvRecord - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 5. AD Replication ------------------------------------------------------
|
|
|
|
function Test-ADReplication {
|
|
Write-Section "Replication"
|
|
|
|
$adModuleLoaded = Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
|
|
if (-not $adModuleLoaded) {
|
|
try { Import-Module ActiveDirectory -ErrorAction Stop } catch {}
|
|
$adModuleLoaded = Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
if ($adModuleLoaded) {
|
|
try {
|
|
$partners = Get-ADReplicationPartnerMetadata -Target $script:DC -ErrorAction Stop
|
|
$failures = $partners | Where-Object { $_.ConsecutiveReplicationFailures -gt 0 }
|
|
if ($failures) {
|
|
$failCount = ($failures | Measure-Object).Count
|
|
Record-Fail "AD replication status" "$failCount partner(s) with failures"
|
|
} else {
|
|
$partnerCount = ($partners | Measure-Object).Count
|
|
Record-Pass "AD replication status" "$partnerCount partner(s), no failures"
|
|
}
|
|
return
|
|
} catch {
|
|
Write-Verbose "Get-ADReplicationPartnerMetadata failed: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
if (Test-CommandExists "repadmin") {
|
|
try {
|
|
$output = repadmin /replsummary $script:DC 2>&1 | Out-String
|
|
if ($output -match "failed") {
|
|
Record-Fail "AD replication status" "repadmin reports failures"
|
|
} elseif ($output -match "Source DSA") {
|
|
Record-Pass "AD replication status" "repadmin reports no failures"
|
|
} else {
|
|
Record-Pass "AD replication status" "repadmin completed"
|
|
}
|
|
} catch {
|
|
Record-Fail "AD replication status" "repadmin error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Skip "AD replication status" "neither AD module nor repadmin available"
|
|
}
|
|
}
|
|
|
|
# -- 6. FSMO Roles ----------------------------------------------------------
|
|
|
|
function Test-FSMORoles {
|
|
Write-Section "FSMO"
|
|
|
|
if (Test-CommandExists "netdom") {
|
|
try {
|
|
$output = netdom query fsmo 2>&1 | Out-String
|
|
$roles = @(
|
|
"Schema master",
|
|
"Domain naming master",
|
|
"PDC",
|
|
"RID pool manager",
|
|
"Infrastructure master"
|
|
)
|
|
$found = 0
|
|
foreach ($role in $roles) {
|
|
if ($output -match "$role\s+\S+") {
|
|
$found++
|
|
}
|
|
}
|
|
if ($found -eq 5) {
|
|
Record-Pass "FSMO roles assigned" "all 5 roles located"
|
|
} elseif ($found -gt 0) {
|
|
Record-Fail "FSMO roles assigned" "only $found of 5 roles found"
|
|
} else {
|
|
Record-Fail "FSMO roles assigned" "no roles found in netdom output"
|
|
}
|
|
} catch {
|
|
Record-Fail "FSMO roles assigned" "netdom error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
$adModuleLoaded = Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
|
|
if ($adModuleLoaded) {
|
|
try {
|
|
$forest = Get-ADForest -ErrorAction Stop
|
|
$domain = Get-ADDomain -ErrorAction Stop
|
|
$roleCount = 0
|
|
if ($forest.SchemaMaster) { $roleCount++ }
|
|
if ($forest.DomainNamingMaster) { $roleCount++ }
|
|
if ($domain.PDCEmulator) { $roleCount++ }
|
|
if ($domain.RIDMaster) { $roleCount++ }
|
|
if ($domain.InfrastructureMaster) { $roleCount++ }
|
|
|
|
if ($roleCount -eq 5) {
|
|
Record-Pass "FSMO roles assigned" "all 5 roles located via AD module"
|
|
} else {
|
|
Record-Fail "FSMO roles assigned" "only $roleCount of 5 roles found"
|
|
}
|
|
} catch {
|
|
Record-Fail "FSMO roles assigned" "AD module error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Skip "FSMO roles assigned" "neither netdom nor AD module available"
|
|
}
|
|
}
|
|
}
|
|
|
|
# -- 7. SYSVOL Share --------------------------------------------------------
|
|
|
|
function Test-SYSVOLShare {
|
|
Write-Section "Shares"
|
|
|
|
$sysvolPath = "\\$($script:DC)\SYSVOL"
|
|
try {
|
|
if (Test-Path $sysvolPath -ErrorAction Stop) {
|
|
Record-Pass "SYSVOL share accessible" $sysvolPath
|
|
} else {
|
|
Record-Fail "SYSVOL share accessible" "$sysvolPath - not accessible"
|
|
}
|
|
} catch {
|
|
Record-Fail "SYSVOL share accessible" "$sysvolPath - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 8. NETLOGON Share ------------------------------------------------------
|
|
|
|
function Test-NETLOGONShare {
|
|
$netlogonPath = "\\$($script:DC)\NETLOGON"
|
|
try {
|
|
if (Test-Path $netlogonPath -ErrorAction Stop) {
|
|
Record-Pass "NETLOGON share accessible" $netlogonPath
|
|
} else {
|
|
Record-Fail "NETLOGON share accessible" "$netlogonPath - not accessible"
|
|
}
|
|
} catch {
|
|
Record-Fail "NETLOGON share accessible" "$netlogonPath - $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# -- 9. Kerberos -----------------------------------------------------------
|
|
|
|
function Test-Kerberos {
|
|
Write-Section "Authentication"
|
|
|
|
try {
|
|
$secure = Test-ComputerSecureChannel -ErrorAction Stop
|
|
if ($secure) {
|
|
Record-Pass "Kerberos secure channel" "secure channel OK"
|
|
} else {
|
|
Record-Fail "Kerberos secure channel" "secure channel broken"
|
|
}
|
|
} catch {
|
|
if (Test-CommandExists "klist") {
|
|
try {
|
|
$klist = klist 2>&1 | Out-String
|
|
if ($klist -match "krbtgt") {
|
|
Record-Pass "Kerberos secure channel" "TGT present (klist)"
|
|
} else {
|
|
Record-Fail "Kerberos secure channel" "no TGT found (klist)"
|
|
}
|
|
} catch {
|
|
Record-Fail "Kerberos secure channel" "klist error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Fail "Kerberos secure channel" $_.Exception.Message
|
|
}
|
|
}
|
|
}
|
|
|
|
# -- 10. Group Policy -------------------------------------------------------
|
|
|
|
function Test-GroupPolicy {
|
|
if (Test-CommandExists "gpresult") {
|
|
try {
|
|
$output = gpresult /r 2>&1 | Out-String
|
|
if ($output -match "Applied Group Policy Objects" -or $output -match "Last time Group Policy was applied") {
|
|
$lastApplied = ""
|
|
if ($output -match "Last time Group Policy was applied:\s*(.+)") {
|
|
$lastApplied = $Matches[1].Trim()
|
|
}
|
|
Record-Pass "Group Policy processing" $(if ($lastApplied) { "last applied: $lastApplied" } else { "policy applied" })
|
|
} elseif ($output -match "ERROR\b|Access is denied") {
|
|
Record-Fail "Group Policy processing" "gpresult returned an error"
|
|
} else {
|
|
Record-Fail "Group Policy processing" "could not confirm policy application"
|
|
}
|
|
} catch {
|
|
Record-Fail "Group Policy processing" "gpresult error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Skip "Group Policy processing" "gpresult not available"
|
|
}
|
|
}
|
|
|
|
# -- 11. AD Web Services (ADWS) --------------------------------------------
|
|
|
|
function Test-ADWS {
|
|
Write-Section "Services"
|
|
|
|
try {
|
|
$tcp = Test-NetConnection -ComputerName $script:DC -Port 9389 -WarningAction SilentlyContinue -ErrorAction Stop
|
|
if ($tcp.TcpTestSucceeded) {
|
|
Record-Pass "AD Web Services" "$($script:DC):9389"
|
|
} else {
|
|
Record-Fail "AD Web Services" "$($script:DC):9389 - port closed"
|
|
}
|
|
} catch {
|
|
if (Test-CommandExists "Test-NetConnection") {
|
|
Record-Fail "AD Web Services" "$($script:DC):9389 - $($_.Exception.Message)"
|
|
} else {
|
|
Record-Skip "AD Web Services" "Test-NetConnection not available"
|
|
}
|
|
}
|
|
}
|
|
|
|
# -- 12. Domain Trusts ------------------------------------------------------
|
|
|
|
function Test-DomainTrusts {
|
|
Write-Section "Trusts"
|
|
|
|
if (Test-CommandExists "nltest") {
|
|
try {
|
|
$output = nltest /trusted_domains 2>&1 | Out-String
|
|
if ($output -match "The command completed successfully") {
|
|
$trustLines = ($output -split "`n") | Where-Object {
|
|
$_ -match "\S" -and
|
|
$_ -notmatch "The command completed" -and
|
|
$_ -notmatch "^$"
|
|
}
|
|
$trustCount = ($trustLines | Measure-Object).Count
|
|
Record-Pass "Domain trusts" "$trustCount trust(s) enumerated"
|
|
} elseif ($output -match "ERROR_NO_SUCH_DOMAIN") {
|
|
Record-Fail "Domain trusts" "domain not found"
|
|
} else {
|
|
Record-Fail "Domain trusts" "nltest failed"
|
|
}
|
|
} catch {
|
|
Record-Fail "Domain trusts" "nltest error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
$adModuleLoaded = Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
|
|
if ($adModuleLoaded) {
|
|
try {
|
|
$trusts = Get-ADTrust -Filter * -ErrorAction Stop
|
|
$trustCount = ($trusts | Measure-Object).Count
|
|
Record-Pass "Domain trusts" "$trustCount trust(s) found"
|
|
} catch {
|
|
Record-Fail "Domain trusts" "Get-ADTrust error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Skip "Domain trusts" "neither nltest nor AD module available"
|
|
}
|
|
}
|
|
}
|
|
|
|
# -- 13. Time Sync ----------------------------------------------------------
|
|
|
|
function Test-TimeSync {
|
|
Write-Section "Time"
|
|
|
|
if (Test-CommandExists "w32tm") {
|
|
try {
|
|
$output = w32tm /monitor /computers:$($script:DC) /nowarn 2>&1 | Out-String
|
|
if ($output -match "NTP:\s*([+-]?\d+\.\d+)s") {
|
|
$skew = [math]::Abs([double]$Matches[1])
|
|
$skewStr = "{0:N2}" -f $skew
|
|
if ($skew -le 5.0) {
|
|
Record-Pass "Time sync" "skew ${skewStr}s against $($script:DC)"
|
|
} else {
|
|
Record-Fail "Time sync" "skew ${skewStr}s exceeds 5s threshold"
|
|
}
|
|
} elseif ($output -match "error|timeout|unreachable" ) {
|
|
Record-Fail "Time sync" "w32tm could not reach $($script:DC)"
|
|
} else {
|
|
Record-Pass "Time sync" "w32tm completed against $($script:DC)"
|
|
}
|
|
} catch {
|
|
Record-Fail "Time sync" "w32tm error - $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Record-Skip "Time sync" "w32tm not available"
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# OUTPUT
|
|
# ============================================================================
|
|
|
|
function Write-Header {
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "TAP version 13"
|
|
} else {
|
|
Write-Host ""
|
|
Write-Color "Windows AD Smoke Tests" "White"
|
|
Write-Host "DC: $($script:DC)"
|
|
Write-Host "Domain: $($script:DomainFQDN)"
|
|
Write-Host "Time: $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')"
|
|
}
|
|
}
|
|
|
|
function Write-Summary {
|
|
$duration = [math]::Floor(((Get-Date) - $script:StartTime).TotalSeconds)
|
|
|
|
if ($OutputFormat -eq "tap") {
|
|
Write-Host "1..$($script:Total)"
|
|
Write-Host "# pass $($script:Pass)"
|
|
Write-Host "# fail $($script:Fail)"
|
|
Write-Host "# skip $($script:Skip)"
|
|
} else {
|
|
Write-Host ""
|
|
$separator = [string]::new([char]0x2500, 40)
|
|
Write-Color $separator "White"
|
|
Write-Color "Summary $($script:DC) ($($script:DomainFQDN))" "White"
|
|
|
|
$summaryLine = " $($script:Pass) passed $($script:Fail) failed $($script:Skip) skipped (${duration}s)"
|
|
Write-Host $summaryLine
|
|
Write-Color $separator "White"
|
|
|
|
if ($script:Fail -eq 0) {
|
|
Write-Color "All tests passed." "Green"
|
|
} else {
|
|
Write-Color "$($script:Fail) test(s) failed." "Red"
|
|
}
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN
|
|
# ============================================================================
|
|
|
|
# Resolve targets
|
|
$script:DC = Resolve-DomainController
|
|
$script:DomainFQDN = Resolve-Domain
|
|
|
|
Write-Header
|
|
|
|
# Run all tests
|
|
Test-DCConnectivity
|
|
Test-LDAPBind
|
|
Test-LDAPS
|
|
Test-DNSSrvRecords
|
|
Test-ADReplication
|
|
Test-FSMORoles
|
|
Test-SYSVOLShare
|
|
Test-NETLOGONShare
|
|
Test-Kerberos
|
|
Test-GroupPolicy
|
|
Test-ADWS
|
|
Test-DomainTrusts
|
|
Test-TimeSync
|
|
|
|
Write-Summary
|
|
|
|
if ($script:Fail -eq 0) { exit 0 } else { exit 1 }
|