Sync all scripts from website downloads — 352 scripts total
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.
This commit is contained in:
@@ -0,0 +1,655 @@
|
||||
###############################################################################
|
||||
#### 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 }
|
||||
Reference in New Issue
Block a user