Testimo.psm1
function Get-WinADDC { [CmdletBinding()] param([string] $Domain = $Env:USERDNSDOMAIN) $Output = Get-ADDomainController -Server $Domain -Filter * -ErrorAction Stop $Output } function Get-WinADDomain { [CmdletBinding()] param([string] $Domain) $Output = Get-ADDomain -Server $Domain -ErrorAction Stop $Output } function Get-WinADForest { [CmdletBinding()] param() $Output = Get-ADForest -ErrorAction Stop $Output } function Out-Begin { param([string] $Text, [int] $Level, [string] $Type = 't', [string] $Domain, [string] $DomainController) if ($Domain -and $DomainController) { if ($Type -eq 't') { [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow } else { [ConsoleColor[]] $Color = [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow } $TestText = "[$Type]", "[$Domain]", "[$($DomainController)] ", $Text } elseif ($Domain) { if ($Type -eq 't') { [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow } else { [ConsoleColor[]] $Color = [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow } $TestText = "[$Type]", "[$Domain] ", $Text } elseif ($DomainController) { Write-Warning "Out-Begin - Shouldn't happen - Fix me." } else { if ($Type -eq 't') { [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow } else { [ConsoleColor[]] $Color = [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow } $TestText = "[$Type]", "[Forest] ", $Text } Write-Color -Text $TestText -Color $Color -StartSpaces $Level -NoNewLine } function Out-Failure { param([string] $Text, [int] $Level, [string] $ExtendedValue = 'Input data not provided. Failing test.', [string] $Domain, [string] $DomainController) Out-Begin -Text $Text -Level $Level Out-Status -Text $Text -Status $false -ExtendedValue $ExtendedValue -Domain $Domain -DomainController $DomainController } function Out-Status { [CmdletBinding()] param([string] $Text, [nullable[bool]] $Status, [string] $Section, [string] $ExtendedValue, [string] $Domain, [string] $DomainController) if ($Status -eq $true) { [string] $TextStatus = 'Pass' [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::Green, [ConsoleColor]::Cyan, [ConsoleColor]::Cyan, [ConsoleColor]::Green, [ConsoleColor]::Cyan } elseif ($Status -eq $false) { [string] $TextStatus = 'Fail' [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::Red, [ConsoleColor]::Cyan, [ConsoleColor]::Cyan, [ConsoleColor]::Red, [ConsoleColor]::Cyan } else { [string] $TextStatus = 'Informative' [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::Cyan, [ConsoleColor]::Cyan, [ConsoleColor]::DarkMagenta, [ConsoleColor]::Cyan } if ($ExtendedValue) { Write-Color -Text ' [', $TextStatus, ']', " [", $ExtendedValue, "]" -Color $Color } else { Write-Color -Text ' [', $TextStatus, ']' -Color $Color } if ($Domain -and $DomainController) { $TestType = 'Domain Controller' $TestText = "Domain Controller - $DomainController | $Text" } elseif ($Domain) { $TestType = 'Domain' $TestText = "Domain - $Domain | $Text" } elseif ($DomainController) { $TestType = 'Should not happen. Find an error.' } else { $TestType = 'Forest Wide' $TestText = "Forest | $Text" } if ($null -ne $Status) { $Script:TestResults.Add([PSCustomObject]@{Test = $TestText TestType = $TestType Domain = $Domain DomainController = $DomainController Status = $Status Extended = $ExtendedValue }) } } function Out-Summary { [CmdletBinding()] param([System.Diagnostics.Stopwatch] $Time, $Text, [int] $Level, [string] $Domain, [string] $DomainController, [PSCustomobject] $TestsSummary) $EndTime = Stop-TimeLog -Time $Time -Option OneLiner $Type = 'i' if ($Domain -and $DomainController) { if ($Type -eq 't') { [ConsoleColor[]] $Color = @([ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray) } else { [ConsoleColor[]] $Color = @([ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray [ConsoleColor]::Yellow, [ConsoleColor]::White, [ConsoleColor]::Yellow [ConsoleColor]::Green [ConsoleColor]::Yellow [ConsoleColor]::Red [ConsoleColor]::Yellow [ConsoleColor]::Cyan) } $TestText = @("[$Type]", "[$Domain]", "[$($DomainController)] ", $Text, ' [', 'Time to execute tests: ', $EndTime, ']', '[', 'Tests Total: ', ($TestsSummary.Total), ', Passed: ', ($TestsSummary.Passed), ', Failed: ', ($TestsSummary.Failed), ', Skipped: ', ($TestsSummary.Skipped), ']') } elseif ($Domain) { if ($Type -eq 't') { [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray } else { [ConsoleColor[]] $Color = @([ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray [ConsoleColor]::Yellow, [ConsoleColor]::White, [ConsoleColor]::Yellow [ConsoleColor]::Green [ConsoleColor]::Yellow [ConsoleColor]::Red [ConsoleColor]::Yellow [ConsoleColor]::Cyan) } $TestText = @("[$Type]", "[$Domain] ", $Text, ' [', 'Time to execute tests: ', $EndTime, ']', '[', 'Tests Total: ', ($TestsSummary.Total), ', Passed: ', ($TestsSummary.Passed), ', Failed: ', ($TestsSummary.Failed), ', Skipped: ', ($TestsSummary.Skipped), ']') } elseif ($DomainController) { Write-Warning "Out-Begin - Shouldn't happen - Fix me." } else { if ($Type -eq 't') { [ConsoleColor[]] $Color = [ConsoleColor]::Cyan, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray } else { [ConsoleColor[]] $Color = @([ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray [ConsoleColor]::Yellow, [ConsoleColor]::White, [ConsoleColor]::Yellow [ConsoleColor]::Green [ConsoleColor]::Yellow [ConsoleColor]::Red [ConsoleColor]::Yellow [ConsoleColor]::Cyan) } $TestText = @("[$Type]", "[Forest] ", $Text, ' [', 'Time to execute tests: ', $EndTime, ']', '[', 'Tests Total: ', ($TestsSummary.Total), ', Passed: ', ($TestsSummary.Passed), ', Failed: ', ($TestsSummary.Failed), ', Skipped: ', ($TestsSummary.Skipped), ']') } Write-Color -Text $TestText -Color $Color -StartSpaces $Level } $Script:SBDomain = { param([string] $Domain) Get-WinADDomain -Domain $Domain } $Script:SBDomainControllers = { param([string] $Domain) Get-WinADDC -Domain $Domain } $Script:SBDomainControllersDiskSpace = { Get-ComputerDiskLogical -ComputerName $DomainController -OnlyLocalDisk -WarningAction SilentlyContinue } $Script:SBDomainControllersDiskSpacePercent = { Test-Value -TestName "Disk Free Percent" -Property 'FreePercent' -PropertyExtendedValue 'FreePercent' @args } $Script:SBDomainControllersDiskSpaceGB = { Test-Value -TestName "Disk Free Size" -Property 'FreeSpace' -PropertyExtendedValue 'FreeSpace' @args } $Script:SBDomainControllersLDAP = { Test-LDAP -ComputerName $DomainController -WarningAction SilentlyContinue } $Script:SBDomainControllersLDAP_Port = { Test-Value -TestName "LDAP Port is Available" -Property 'LDAP' @args } $Script:SBDomainControllersLDAP_PortSSL = { Test-Value -TestName "LDAP SSL Port is Available" -Property 'LDAPS' @args } $Script:SBDomainControllersLDAP_PortGC = { Test-Value -TestName "LDAP GC Port is Available" -Property 'GlobalCatalogLDAP' @args } $Script:SBDomainControllersLDAP_PortGC_SSL = { Test-Value -TestName "LDAP GC SSL Port is Available" -Property 'GlobalCatalogLDAPS' @args } $Script:SBDomainControllersPing = { Test-NetConnection -ComputerName $DomainController -WarningAction SilentlyContinue } $Script:SBDomainControllersPingTest = { Test-Value -TestName "Responds to PING" -Property 'PingSucceeded' -PropertyExtendedValue 'PingReplyDetails', 'RoundtripTime' @args } $Script:SBDomainControllersPort53 = { Test-NetConnection -ComputerName $DomainController -WarningAction SilentlyContinue -Port 53 } $Script:SBDomainControllersPort53Test = { Test-Value -TestName "Port is open" -Property 'TcpTestSucceeded' @args } $Script:SBDomainControllersRespondsPS = { Get-WinADDomain -Domain $DomainController } $Script:SBDomainControllersServices = { $Services = @('ADWS', 'DNS', 'DFS', 'DFSR', 'Eventlog', 'EventSystem', 'KDC', 'LanManWorkstation', 'LanManServer', 'NetLogon', 'NTDS', 'RPCSS', 'SAMSS', 'W32Time') Get-PSService -Computers $DomainController -Services $Services } $Script:SBDomainControllersServicesTestStatus = { foreach ($_ in $Object) { Test-Value -TestName "Service $($_.Name) Status" -Property 'Status' -Object $_ -ExpectedValue 'Running' -Level $LevelTest -Domain $Domain -DomainController $DomainController } } $Script:SBDomainControllersServicesTestStartType = { foreach ($_ in $Object) { Test-Value -TestName "Service $($_.Name) Start Type" -Property 'StartType' -Object $_ -ExpectedValue 'Automatic' -Level $LevelTest -Domain $Domain -DomainController $DomainController } } $Script:SBDomainDNSForwaders = { $PSWinDocumentationDNS = Import-Module PSWinDocumentation.DNS -PassThru & $PSWinDocumentationDNS { param($Domain) $Forwarders = Get-WinDnsServerForwarder -Domain $Domain -WarningAction SilentlyContinue Compare-MultipleObjects -Objects $Forwarders -FormatOutput -CompareSorted:$true -ExcludeProperty GatheredFrom -SkipProperties -Property 'IpAddress' } $Domain } $Script:SBDomainDNSForwadersTest = { Test-Value -TestName 'Same DNS Forwarders' -Property 'Status' @args } $Script:SBDomainPasswordComplexity = { $ADModule = Import-Module PSWinDocumentation.AD -PassThru & $ADModule { param($Domain); Get-WinADDomainDefaultPasswordPolicy -Domain $Domain } $Domain } $Script:SBDomainPasswordComplexityTest1 = { Test-Value -TestName 'Complexity Enabled' -Property 'Complexity Enabled' @args } $Script:SBDomainPasswordComplexityTest2 = { Test-Value -TestName 'Lockout Duration' -Property 'Lockout Duration' @args } $Script:SBDomainPasswordComplexityTest3 = { Test-Value -TestName 'Lockout Observation Window' -Property 'Lockout Observation Window' @args } $Script:SBDomainPasswordComplexityTest4 = { Test-Value -TestName 'Lockout Threshold' -Property 'Lockout Threshold' @args } $Script:SBDomainPasswordComplexityTest5 = { Test-Value -TestName 'Max Password Age' -Property 'Max Password Age' @args } $Script:SBDomainPasswordComplexityTest6 = { Test-Value -TestName 'Min Password Length' -Property 'Min Password Length' @args } $Script:SBDomainPasswordComplexityTest7 = { Test-Value -TestName 'Min Password Age' -Property 'Min Password Age' @args } $Script:SBDomainPasswordComplexityTest8 = { Test-Value -TestName 'Password History Count' -Property 'Password History Count' @args } $Script:SBDomainPasswordComplexityTest9 = { Test-Value -TestName 'Reversible Encryption Enabled' -Property 'Reversible Encryption Enabled' @args } $Script:SBDomainScavenging = { $PSWinDocumentationDNS = Import-Module PSWinDocumentation.DNS -PassThru & $PSWinDocumentationDNS { param($Domain) $Object = Get-WinDnsServerScavenging -Domain $Domain $Object | Where-Object { $_.ScavengingInterval -ne 0 -and $null -ne $_.ScavengingInterval } } $Domain } $Script:SBDomainScavengingTest0 = { Test-Value -TestName 'Scavenging DNS Servers Count' @args } $Script:SBDomainScavengingTest1 = { Test-Value -TestName 'Scavenging Interval' -Property 'ScavengingInterval', 'Days' @args } $Script:SBDomainScavengingTest2 = { Test-Value -TestName 'Scavenging State' -Property 'ScavengingState' @args } $Script:SBDomainScavengingTest3 = { Test-Value -TestName 'Last Scavenge Time' -Property 'LastScavengeTime' @args } $Script:SBDomainTimeSynchronizationInternal = { Get-ComputerTime -TimeTarget $DomainController -WarningAction SilentlyContinue } $Script:SBDomainTimeSynchronizationExternal = { Get-ComputerTime -TimeTarget $DomainController -TimeSource 'pool.ntp.org' -WarningAction SilentlyContinue } $Script:SBDomainTimeSynchronizationTest1 = { Test-Value -TestName 'Time Difference' -Property 'TimeDifferenceSeconds' @args } $Script:SBDomainTrustsData = { $ADModule = Import-Module PSWinDocumentation.AD -PassThru & $ADModule { param($Domain) Get-WinADDomainTrusts -Domain $Domain } $Domain @args } $Script:SBDomainTrustsConnectivity = { foreach ($_ in $Object) { Test-Value -TestName "Trust status verification | Source $Domain, Target $($_.'Trust Target'), Direction $($_.'Trust Direction')" -Property 'Trust Status' -ExpectedValue 'OK' -Object $_ -Level $LevelTest -Domain $Domain -DomainController $DomainController } } $Script:SBDomainTrustsUnconstrainedDelegation = { foreach ($_ in $Object) { if ($($_.'Trust Direction' -eq 'BiDirectional' -or $_.'Trust Direction' -eq 'InBound')) { Test-Value -TestName "Trust Unconstrained TGTDelegation | Source $Domain, Target $($_.'Trust Target'), Direction $($_.'Trust Direction')" -Property 'TGTDelegation' -ExpectedValue $True -Object $_ -Level $LevelTest -Domain $Domain -DomainController $DomainController } } } $Script:SBForest = { Get-WinADForest } $Script:SBForestLastBackup = { Get-WinADLastBackup } $Script:SBForestLastBackupTest = { foreach ($_ in $Object) { Test-Value -Level $LevelTest -TestName "Last Backup $($_.NamingContext)" -Object $_ -Property 'LastBackupDaysAgo' -PropertyExtendedValue 'LastBackup' -lt -ExpectedValue 2 -Domain $Domain -DomainController $DomainController } } $Script:SBForestOptionalFeatures = { $ADModule = Import-Module PSWinDocumentation.AD -PassThru & $ADModule { Get-WinADForestOptionalFeatures -WarningAction SilentlyContinue } } $Script:SBForestOptionalFeaturesTest1 = { Test-Value -TestName 'Recycle Bin Enabled' -Property 'Recycle Bin Enabled' @args } $Script:SBForestOptionalFeaturesTest2 = { Test-Value -TestName 'LAPS Schema Extended' -Property 'Laps Enabled' @args } $Script:SBForestOptionalFeaturesTest3 = { Test-Value -TestName 'Privileged Access Management Enabled' -Property 'Privileged Access Management Feature Enabled' @args } $Script:SBForestReplication = { Get-WinADForestReplication -WarningAction SilentlyContinue } $Script:SBForestReplicationTest1 = { foreach ($_ in $Object) { Test-Value -TestName "Replication from $($_.Server) to $($_.ServerPartner)" -Property 'Status' -PropertyExtendedValue 'StatusMessage' -ExpectedValue $True -Level $LevelTest -Object $_ -Domain $Domain -DomainController $DomainController } } function Start-Testing { [CmdletBinding()] param([ScriptBlock] $Execute, [string] $Scope, [string] $Domain, [string] $DomainController) $GlobalTime = Start-TimeLog if ($Scope -eq 'Forest') { $Level = 3 $LevelTest = 6 $LevelSummary = 3 $LevelTestFailure = 6 } elseif ($Scope -eq 'Domain') { $Level = 6 $LevelTest = 9 $LevelSummary = 6 $LevelTestFailure = 9 } elseif ($Scope -eq 'DomainControllers') { $Level = 9 $LevelTest = 12 $LevelSummary = 9 $LevelTestFailure = 12 } else { } if ($Domain -and $DomainController) { $SummaryText = "Domain $Domain, $DomainController" } elseif ($Domain) { Write-Color $SummaryText = "Domain $Domain" } else { $SummaryText = "Forest" } Out-Begin -Type 'i' -Text $SummaryText -Level ($LevelSummary - 3) -Domain $Domain -DomainController $DomainController Out-Status -Text $SummaryText -Status $null -ExtendedValue '' -Domain $Domain -DomainController $DomainController $TestsSummaryTogether = @(foreach ($Source in $($Script:TestimoConfiguration.$Scope.Sources.Keys)) { $CurrentSource = $Script:TestimoConfiguration.$Scope.Sources[$Source] if ($CurrentSource['Enable'] -eq $true) { [Array] $AllTests = $CurrentSource['Tests'].Keys $TestsSummary = [PSCustomobject] @{Passed = 0 Failed = 0 Skipped = 0 Total = 0 } $Time = Start-TimeLog $Object = Start-TestProcessing -Test $CurrentSource['SourceName'] -Level $Level -OutputRequired -Domain $Domain -DomainController $DomainController { & $CurrentSource['SourceData'] -DomainController $DomainController -Domain $Domain } if ($Object) { $FailAllTests = $false Out-Begin -Text $CurrentSource['SourceName'] -Level $LevelTest -Domain $Domain -DomainController $DomainController Out-Status -Text $CurrentSource['SourceName'] -Status $true -ExtendedValue 'Data is available.' -Domain $Domain -DomainController $DomainController $TestsSummary.Passed = $TestsSummary.Passed + 1 } else { $FailAllTests = $true Out-Failure -Text $CurrentSource['SourceName'] -Level $LevelTest -ExtendedValue 'No data available.' -Domain $Domain -DomainController $DomainController $TestsSummary.Failed = $TestsSummary.Failed + 1 } foreach ($Test in $AllTests) { $CurrentTest = $CurrentSource['Tests'][$Test] if ($CurrentTest['Enable'] -eq $True) { if (-not $FailAllTests) { if ($CurrentTest['TestParameters']) { $Parameters = $CurrentTest['TestParameters'] } else { $Parameters = $null } $TestsResults = Start-TestingTest -Test $CurrentTest['TestName'] -Level $LevelTest -Domain $Domain -DomainController $DomainController { & $CurrentTest['TestSource'] -Object $Object -Domain $Domain -DomainController $DomainController @Parameters -Level $LevelTest } $TestsSummary.Passed = $TestsSummary.Passed + ($TestsResults | Where-Object { $_ -eq $true }).Count $TestsSummary.Failed = $TestsSummary.Failed + ($TestsResults | Where-Object { $_ -eq $false }).Count } else { $TestsSummary.Failed = $TestsSummary.Failed + 1 Out-Failure -Text $CurrentTest['TestName'] -Level $LevelTestFailure -Domain $Domain -DomainController $DomainController } } else { $TestsSummary.Skipped = $TestsSummary.Skipped + 1 } } $TestsSummary.Total = $TestsSummary.Failed + $TestsSummary.Passed + $TestsSummary.Skipped $TestsSummary Out-Summary -Text $CurrentSource['SourceName'] -Time $Time -Level $LevelSummary -Domain $Domain -DomainController $DomainController -TestsSummary $TestsSummary } } if ($Execute) { & $Execute }) $TestsSummaryFinal = [PSCustomObject] @{Passed = ($TestsSummaryTogether.Passed | Measure-Object -Sum).Sum Failed = ($TestsSummaryTogether.Failed | Measure-Object -Sum).Sum Skipped = ($TestsSummaryTogether.Skipped | Measure-Object -Sum).Sum Total = ($TestsSummaryTogether.Total | Measure-Object -Sum).Sum } $TestsSummaryFinal Out-Summary -Text $SummaryText -Time $GlobalTime -Level ($LevelSummary - 3) -Domain $Domain -DomainController $DomainController -TestsSummary $TestsSummaryFinal } function Start-TestingTest { [CmdletBinding()] param([ScriptBlock] $Execute, $Test, [int] $Level, [string] $Domain, [string] $DomainController) if ($Execute) { if ($Script:TestimoConfiguration.Debug.DisableTryCatch) { [Array] $Output = & $Execute $Output } else { try { [Array] $Output = & $Execute $Output } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " } if (-not $ErrorMessage) { } else { Out-Failure -Text $CurrentTest['TestName'] -Level $Level -ExtendedValue $ErrorMessage -Domain $Domain -DomainController $DomainController } } } } function Start-TestProcessing { [CmdletBinding()] param([ScriptBlock] $Execute, [string] $Test, [switch] $OutputRequired, [nullable[bool]] $ExpectedStatus, [int] $Level = 0, [switch] $IsTest, [switch] $Simple, [string] $Domain, [string] $DomainController) if ($Execute) { if ($IsTest) { Out-Begin -Type 't' -Text $Test -Level $Level -Domain $Domain -DomainController $DomainController } else { Out-Begin -Type 'i' -Text $Test -Level $Level -Domain $Domain -DomainController $DomainController } if ($Script:TestimoConfiguration.Debug.DisableTryCatch) { [Array] $Output = & $Execute $ErrorMessage = $null } else { try { [Array] $Output = & $Execute } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " } } if (-not $ErrorMessage) { foreach ($O in $Output) { if ($OutputRequired.IsPresent) { if ($O['Output']) { foreach ($_ in $O['Output']) { $_ } } else { foreach ($_ in $O) { $_ } } } } if ($null -eq $ExpectedStatus) { $TestResult = $null } else { $TestResult = $ExpectedStatus -eq $Output.Status } Out-Status -Text $Test -Status $TestResult -ExtendedValue $O.Extended -Domain $Domain -DomainController $DomainController } else { Out-Status -Text $Test -Status $false -ExtendedValue $ErrorMessage -Domain $Domain -DomainController $DomainController } } } function Test-ImoAD { [CmdletBinding()] param([switch] $ReturnResults) $global:ProgressPreference = 'SilentlyContinue' $Script:TestResults = [System.Collections.Generic.List[PSCustomObject]]::new() $Forest = & $Script:SBForest $null = Start-Testing -Scope 'Forest' { foreach ($Domain in $Forest.Domains) { $Domain = $Domain.ToLower() $null = & $Script:SBDomain -Domain $Domain Start-Testing -Scope 'Domain' -Domain $Domain { $DomainControllers = & $Script:SBDomainControllers -Domain $Domain foreach ($DomainController in $DomainControllers) { $DomainControllerHostName = $($DomainController.HostName).ToLower() Start-Testing -Scope 'DomainControllers' -Domain $Domain -DomainController $DomainControllerHostName } } } } if ($ReturnResults) { $Script:TestResults } } $Script:TestimoConfiguration = [ordered] @{Forest = @{Sources = [ordered] @{OptionalFeatures = @{Enable = $true SourceName = 'Optional Features' SourceData = $Script:SBForestOptionalFeatures Tests = [ordered] @{RecycleBinEnabled = @{Enable = $true TestName = 'Recycle Bin Enabled' TestSource = $Script:SBForestOptionalFeaturesTest1 TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } LapsAvailable = @{Enable = $true TestName = 'Laps Available' TestSource = $Script:SBForestOptionalFeaturesTest2 TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } PrivAccessManagement = @{Enable = $true TestName = 'Privileged Access Management' TestSource = $Script:SBForestOptionalFeaturesTest3 TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } } } Replication = @{Enable = $true SourceName = 'Forest Replication' SourceData = $Script:SBForestReplication Tests = [ordered] @{ReplicationTests = @{Enable = $true TestName = 'Replication Test' TestSource = $Script:SBForestReplicationTest1 TestParameters = @{OperationType = 'eq' } } } } LastBackup = @{Enable = $true SourceName = 'Forest Backup' SourceData = $Script:SBForestLastBackup Tests = [ordered] @{LastBackupTests = @{Enable = $true TestName = 'Forest Last Backup Time - Context' TestSource = $Script:SBForestLastBackupTest TestParameters = @{ } } } } } } Domain = @{Sources = [ordered] @{PasswordComplexity = @{Enable = $true SourceName = 'Password Complexity Requirements' SourceData = $Script:SBDomainPasswordComplexity Tests = [ordered] @{ComplexityEnabled = @{Enable = $true TestName = 'Complexity Enabled' TestSource = $Script:SBDomainPasswordComplexityTest1 TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } 'Lockout Duration' = @{Enable = $true TestName = 'Lockout Duration' TestSource = $Script:SBDomainPasswordComplexityTest2 TestParameters = @{ExpectedValue = 30 OperationType = 'ge' } } 'Lockout Observation Window' = @{Enable = $true TestName = 'Lockout Observation Window' TestSource = $Script:SBDomainPasswordComplexityTest3 TestParameters = @{ExpectedValue = 30 OperationType = 'ge' } } 'Lockout Threshold' = @{Enable = $true TestName = 'Lockout Threshold' TestSource = $Script:SBDomainPasswordComplexityTest4 TestParameters = @{ExpectedValue = 5 OperationType = 'gt' } } 'Max Password Age' = @{Enable = $true TestName = 'Max Password Age' TestSource = $Script:SBDomainPasswordComplexityTest5 TestParameters = @{ExpectedValue = 60 OperationType = 'le' } } 'Min Password Length' = @{Enable = $true TestName = 'Min Password Length' TestSource = $Script:SBDomainPasswordComplexityTest6 TestParameters = @{ExpectedValue = 8 OperationType = 'gt' } } 'Min Password Age' = @{Enable = $true TestName = 'Min Password Age' TestSource = $Script:SBDomainPasswordComplexityTest7 TestParameters = @{ExpectedValue = 1 OperationType = 'le' } } 'Password History Count' = @{Enable = $true TestName = 'Password History Count' TestSource = $Script:SBDomainPasswordComplexityTest8 TestParameters = @{ExpectedValue = 10 OperationType = 'ge' } } 'Reversible Encryption Enabled' = @{Enable = $true TestName = 'Reversible Encryption Enabled' TestSource = $Script:SBDomainPasswordComplexityTest9 TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } } } Trusts = @{Enable = $true SourceName = "Trust Availability" SourceData = $Script:SBDomainTrustsData Tests = [ordered] @{TrustsConnectivity = @{Enable = $true TestName = 'Trust status verification' TestSource = $Script:SBDomainTrustsConnectivity } TrustsUnconstrainedDelegation = @{Enable = $true TestName = 'Trust Unconstrained TGTDelegation' TestSource = $Script:SBDomainTrustsUnconstrainedDelegation } } } DNSScavengingForPrimaryDNSServer = @{Enable = $true SourceName = "DNS Scavenging - Primary DNS Server" SourceData = $Script:SBDomainScavenging Tests = [ordered] @{'Scavenging Count' = @{Enable = $true TestName = 'Scavenging Count' TestSource = $Script:SBDomainScavengingTest0 TestParameters = @{ExpectedCount = 1 OperationType = 'eq' } Explanation = 'Scavenging Count should be 1. There should be 1 DNS server per domain responsible for scavenging. If this returns false, every other test fails.' } 'Scavenging Interval' = @{Enable = $true TestName = 'Scavenging Interval' TestSource = $Script:SBDomainScavengingTest1 TestParameters = @{ExpectedValue = 7 OperationType = 'le' } } 'Scavenging State' = @{Enable = $true TestName = 'Scavenging State' TestSource = $Script:SBDomainScavengingTest2 TestParameters = @{ExpectedValue = $true OperationType = 'eq' } Explanation = 'Scavenging State is responsible for enablement of scavenging for all new zones created.' RecommendedValue = $true ExplanationRecommended = 'It should be enabled so all new zones are subject to scavanging.' DefaultValue = $false } 'Last Scavenge Time' = @{Enable = $true TestName = 'Last Scavenge Time' TestSource = $Script:SBDomainScavengingTest3 TestParameters = @{ExpectedValue = (Get-Date).AddDays(-7) OperationType = 'lt' } } } } DNSForwaders = @{Enable = $true SourceName = "DNS Forwarders" SourceData = $Script:SBDomainDNSForwaders Tests = [ordered] @{SameForwarders = @{Enable = $true TestName = 'Same DNS Forwarders' TestSource = $Script:SBDomainDNSForwadersTest TestParameters = @{ExpectedValue = $true OperationType = 'eq' } Explanation = 'DNS forwarders within one domain should have identical setup' } } } } } DomainControllers = @{Sources = [ordered] @{RespondsToPowerShellQueries = @{Enable = $true SourceName = "Responds to PowerShell Queries" SourceData = $Script:SBDomainControllersRespondsPS } Services = @{Enable = $true SourceName = 'Service Status' SourceData = $Script:SBDomainControllersServices Tests = [ordered] @{ServiceStatus = @{Enable = $true TestName = 'Service is RUNNING' TestSource = $Script:SBDomainControllersServicesTestStatus TestParameters = @{ExpectedValue = 'Running' OperationType = 'eq' } } ServiceStartType = @{Enable = $true TestName = 'Service START TYPE is Automatic' TestSource = $Script:SBDomainControllersServicesTestStartType TestParameters = @{ExpectedValue = 'Automatic' OperationType = 'eq' } } } } LDAP = @{Enable = $true SourceName = 'LDAP Connectivity' SourceData = $Script:SBDomainControllersLDAP Tests = [ordered] @{PortLDAP = @{Enable = $true TestName = 'LDAP Port is Available' TestSource = $Script:SBDomainControllersLDAP_Port TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } PortLDAPS = @{Enable = $true TestName = 'LDAP SSL Port is Available' TestSource = $Script:SBDomainControllersLDAP_PortSSL TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } PortLDAP_GC = @{Enable = $true TestName = 'LDAP GC Port is Available' TestSource = $Script:SBDomainControllersLDAP_PortGC TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } PortLDAPS_GC = @{Enable = $true TestName = 'LDAP SSL GC Port is Available' TestSource = $Script:SBDomainControllersLDAP_PortGC_SSL TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } } } Pingable = @{Enable = $true SourceName = 'Ping Connectivity' SourceData = $Script:SBDomainControllersPing Tests = @{Ping = @{Enable = $true TestName = 'Responding to PING' TestSource = $Script:SBDomainControllersPingTest TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } } } Port53 = @{Enable = $true SourceName = 'PORT 53 (DNS)' SourceData = $Script:SBDomainControllersPort53 Tests = @{Ping = @{Enable = $true TestName = 'Port 53 is OPEN' TestSource = $Script:SBDomainControllersPort53Test TestParameters = @{ExpectedValue = $true OperationType = 'eq' } } } } DiskSpace = @{Enable = $true SourceName = 'Disk Free' SourceData = $Script:SBDomainControllersDiskSpace Tests = @{FreeSpace = @{Enable = $true TestName = 'Free Space in GB' TestSource = $Script:SBDomainControllersDiskSpaceGB TestParameters = @{ExpectedValue = 30 OperationType = 'gt' } } FreePercent = @{Enable = $true TestName = 'Free Space Percent' TestSource = $Script:SBDomainControllersDiskSpacePercent TestParameters = @{ExpectedValue = 20 OperationType = 'gt' } } } } TimeSynchronizationInternal = @{Enable = $true SourceName = "Time Synchronization Internal" SourceData = $Script:SBDomainTimeSynchronizationInternal Tests = [ordered] @{TimeSynchronizationTest = @{Enable = $true TestName = 'Time Difference' TestSource = $Script:SBDomainTimeSynchronizationTest1 TestParameters = @{ExpectedValue = 1 OperationType = 'le' } } } MicrosoftMaterials = 'https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc773263(v=ws.10)#w2k3tr_times_tools_uhlp' } TimeSynchronizationExternal = @{Enable = $true SourceName = "Time Synchronization External" SourceData = $Script:SBDomainTimeSynchronizationExternal Tests = [ordered] @{TimeSynchronizationTest = @{Enable = $true TestName = 'Time Difference' TestSource = $Script:SBDomainTimeSynchronizationTest1 TestParameters = @{ExpectedValue = 1 OperationType = 'le' } } } MicrosoftMaterials = 'https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc773263(v=ws.10)#w2k3tr_times_tools_uhlp' } } } AnyServers = @{Sources = [ordered] @{Services = @{Enable = $false SourceName = 'Service Status' SourceData = $Script:SBDomainControllersServices Tests = [ordered] @{ServiceStatus = @{Enable = $true TestName = 'Service is RUNNING' TestSource = $Script:SBDomainControllersServicesTestStatus TestParameters = @{ExpectedValue = 'Running' OperationType = 'eq' } } ServiceStartType = @{Enable = $true TestName = 'Service START TYPE is Automatic' TestSource = $Script:SBDomainControllersServicesTestStartType TestParameters = @{ExpectedValue = 'Automatic' OperationType = 'eq' } } } } } } Debug = @{DisableTryCatch = $false } } function Test-Me { param([string] $OperationType, [string] $TestName, [int] $Level, [string] $Domain, [string] $DomainController, [string[]] $Property, [Object] $TestedValue, [Object] $Object, [Object] $ExpectedValue, [string[]] $PropertyExtendedValue, [int] $ExpectedCount) Out-Begin -Text $TestName -Level $Level -Domain $Domain -DomainController $DomainController $TestedValue = $Object foreach ($V in $Property) { $TestedValue = $TestedValue.$V } $ScriptBlock = { $Operators = @{'lt' = 'LessThan' 'gt' = 'GreaterThan' 'le' = 'LessOrEqual' 'ge' = 'GreaterOrEqual' 'eq' = 'Equal' } if ($ExpectedCount) { if ($OperationType -eq 'lt') { $TestResult = $Object.Count -lt $ExpectedCount } elseif ($OperationType -eq 'gt') { $TestResult = $Object.Count -lt $ExpectedCount } elseif ($OperationType -eq 'ge') { $TestResult = $Object.Count -lt $ExpectedCount } elseif ($OperationType -eq 'le') { $TestResult = $Object.Count -lt $ExpectedCount } else { $TestResult = $Object.Count -lt $ExpectedCount } $TextTestedValue = $Object.Count $ExpectedValue = $ExpectedCount } elseif ($ExpectedValue) { if ($null -eq $TestedValue -and $null -ne $ExpectedValue) { $TestResult = $false $TextTestedValue = 'Null' } else { if ($OperationType -eq 'lt') { $TestResult = $TestedValue -lt $ExpectedValue } elseif ($OperationType -eq 'gt') { $TestResult = $TestedValue -gt $ExpectedValue } elseif ($OperationType -eq 'ge') { $TestResult = $TestedValue -ge $ExpectedValue } elseif ($OperationType -eq 'le') { $TestResult = $TestedValue -le $ExpectedValue } else { $TestResult = $TestedValue -eq $ExpectedValue } $TextTestedValue = $TestedValue } } else { $TestResult = $null $ExtendedTextValue = "Test provided but no tests required." } if ($TestResult -eq $true) { $Extended = "Expected value ($($Operators[$OperationType])): $($ExpectedValue)" } elseif ($TestResult -eq $false) { $Extended = "Expected value ($($Operators[$OperationType])): $ExpectedValue, Found value: $($TextTestedValue)" } else { $Extended = $ExtendedTextValue } if ($PropertyExtendedValue.Count -gt 0) { $Extended = $Object foreach ($V in $PropertyExtendedValue) { $Extended = $Extended.$V } } Out-Status -Text $TestName -Status $TestResult -ExtendedValue $Extended -Domain $Domain -DomainController $DomainController return $TestResult } if ($Script:TestimoConfiguration.Debug.DisableTryCatch) { & $ScriptBlock } else { try { & $ScriptBlock } catch { Out-Status -Text $TestName -Status $false -ExtendedValue $_.Exception.Message -Domain $Domain -DomainController $DomainController return $False } } } function Test-Value { [CmdletBinding()] param([Object] $Object, [string] $TestName, [string[]] $Property, [Object] $ExpectedValue, [string[]] $PropertyExtendedValue, [switch] $lt, [switch] $gt, [switch] $le, [switch] $eq, [switch] $ge, [string] $OperationType, [int] $Level, [string] $Domain, [Object] $DomainController, [int] $ExpectedCount) if (-not $OperationType) { if ($lt) { $OperationType = 'lt' } elseif ($gt) { $OperationType = 'gt' } elseif ($ge) { $OperationType = 'ge' } elseif ($le) { $OperationType = 'le' } else { $OperationType = 'eq' } } if ($Object) { if ($ExpectedCount) { Test-Me -Object $Object -ExpectedCount $ExpectedCount -OperationType $OperationType -TestName $TestName -Level $Level -Domain $Domain -DomainController $DomainController -Property $Property -ExpectedValue $ExpectedValue -PropertyExtendedValue $PropertyExtendedValue } else { foreach ($_ in $Object) { Test-Me -Object $_ -OperationType $OperationType -TestName $TestName -Level $Level -Domain $Domain -DomainController $DomainController -Property $Property -ExpectedValue $ExpectedValue -PropertyExtendedValue $PropertyExtendedValue } } } else { Write-Warning 'Objected not passed to Test-VALUE.' } } Export-ModuleMember -Function @('Test-ImoAD') -Alias @() |