Testimo.psm1

$ComputersUnsupported = @{Enable = $true
    Source                       = @{Name = "Computers Unsupported"
        Data                              = { $Computers = Get-ADComputer -Filter { (operatingsystem -like "*xp*") -or (operatingsystem -like "*vista*") -or (operatingsystem -like "*Windows NT*") -or (operatingsystem -like "*2000*") -or (operatingsystem -like "*2003*") } -Property Name, OperatingSystem, OperatingSystemServicePack, lastlogontimestamp -Server $Domain
            $Computers | Select-Object Name, OperatingSystem, OperatingSystemServicePack, @{name = "lastlogontimestamp"; expression = { [datetime]::fromfiletime($_.lastlogontimestamp) } } }
        Details                           = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = 'Computers running an unsupported operating system.'
            Resolution                    = 'Upgrade or remove computers from Domain.'
            Resources                     = @()
        }
        ExpectedOutput                    = $false
    }
}
$ComputersUnsupportedMainstream = @{Enable = $true
    Source                                 = @{Name = "Computers Unsupported Mainstream Only"
        Data                                        = { $Computers = Get-ADComputer -Filter { (operatingsystem -like "*2008*") } -Property Name, OperatingSystem, OperatingSystemServicePack, lastlogontimestamp -Server $Domain
            $Computers | Select-Object Name, OperatingSystem, OperatingSystemServicePack, @{name = "lastlogontimestamp"; expression = { [datetime]::fromfiletime($_.lastlogontimestamp) } } }
        Details                                     = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = 'Computers running an unsupported operating system, but with possibly Microsoft support.'
            Resolution                    = 'Consider upgrading computers running Windows Server 2008 or Windows Server 2008 R2 to a version that still offers mainstream support from Microsoft.'
            Resources                     = @()
        }
        ExpectedOutput                              = $false
    }
}
$DHCPAuthorized = @{Enable = $false
    Source                 = @{Name = "DHCP authorized in domain"
        Data                        = { $SearchBase = 'cn=configuration,{0}' -f $DomainInformation.DistinguishedName
            Get-ADObject -SearchBase $searchBase -Filter "objectclass -eq 'dhcpclass' -AND Name -ne 'dhcproot'" }
        Details                     = [ordered] @{Area = 'Configuration'
            Category                   = 'DHCP'
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ""
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                  = [ordered] @{DHCPAuthorized = @{Enable = $true
            Name                                                   = 'At least 1 DHCP Server Authorized'
            Parameters                                             = @{ExpectedCount = '1'
                OperationType                                       = 'gt'
            }
        }
    }
}
$DNSForwaders = @{Enable = $true
    Source               = @{Name = "DNS Forwarders"
        Data                      = { $PSWinDocumentationDNS = Import-PrivateModule PSWinDocumentation.DNS
            & $PSWinDocumentationDNS { param($Domain)
                [Array] $Forwarders = Get-WinDnsServerForwarder -Domain $Domain -WarningAction SilentlyContinue
                if ($Forwarders.Count -gt 1) { Compare-MultipleObjects -Objects $Forwarders -FormatOutput -CompareSorted:$true -ExcludeProperty GatheredFrom -SkipProperties -Property 'IpAddress' -WarningAction SilentlyContinue } else {
                    [PSCustomObject] @{Source = $Forwarders[0].IPAddress -join ', '
                        Status                = $true
                    }
                } } $Domain }
        Details                   = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                = [ordered] @{SameForwarders = @{Enable = $true
            Name                                                 = 'Same DNS Forwarders'
            Parameters                                           = @{Property = 'Status'
                ExpectedValue                                  = $true
                OperationType                                  = 'eq'
                PropertyExtendedValue                          = 'Source'
            }
            Description                                          = 'DNS forwarders within one domain should have identical setup'
        }
    }
}
$DNSScavengingForPrimaryDNSServer = @{Enable = $true
    Source                                   = @{Name = "DNS Scavenging - Primary DNS Server"
        Data                                          = { $PSWinDocumentationDNS = Import-PrivateModule PSWinDocumentation.DNS
            & $PSWinDocumentationDNS { param($Domain)
                $Object = Get-WinDnsServerScavenging -Domain $Domain
                $Object | Where-Object { $_.ScavengingInterval -ne 0 -and $null -ne $_.ScavengingInterval } } $Domain }
        Details                                       = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                                    = [ordered] @{ScavengingCount = @{Enable = $true
            Name                                                                      = 'Scavenging DNS Servers Count'
            Parameters                                                                = @{ExpectedCount = 1
                OperationType                                        = 'eq'
            }
            Description                                                               = 'Scavenging Count should be 1. There should be 1 DNS server per domain responsible for scavenging. If this returns false, every other test fails.'
        }
        ScavengingInterval                                                 = @{Enable = $true
            Name                                   = 'Scavenging Interval'
            Parameters                             = @{Property = 'ScavengingInterval', 'Days'
                ExpectedValue                      = 7
                OperationType                      = 'le'
            }
        }
        'Scavenging State'                                                 = @{Enable = $true
            Name                                   = 'Scavenging State'
            Parameters                             = @{Property = 'ScavengingState'
                ExpectedValue                      = $true
                OperationType                      = 'eq'
            }
            Description                            = 'Scavenging State is responsible for enablement of scavenging for all new zones created.'
            RecommendedValue                       = $true
            DescriptionRecommended                 = 'It should be enabled so all new zones are subject to scavanging.'
            DefaultValue                           = $false
        }
        'Last Scavenge Time'                                               = @{Enable = $true
            Name                                   = 'Last Scavenge Time'
            Parameters                             = @{Property = 'LastScavengeTime'
                ExpectedValue                        = '(Get-Date).AddDays(-7)'
                OperationType                        = 'gt'
            }
        }
    }
}
$DnsZonesAging = @{Enable = $true
    Source                = @{Name = "Aging primary DNS Zone"
        Data                       = { $PSWinDocumentationDNS = Import-PrivateModule PSWinDocumentation.DNS
            & $PSWinDocumentationDNS { param($Domain)
                $Zones = Get-WinDnsServerZones -ZoneName $Domain -Domain $Domain
                Compare-MultipleObjects -Objects $Zones -FormatOutput -CompareSorted:$true -ExcludeProperty GatheredFrom -SkipProperties -Property 'AgingEnabled' } $Domain }
        Details                    = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                 = [ordered] @{EnabledAgingEnabled = @{Enable = $true
            Name                                                       = 'Zone DNS aging should be enabled'
            Parameters                                                 = @{Property = 'Source'
                ExpectedValue                                       = $true
                OperationType                                       = 'eq'
            }
            Description                                                = 'Primary DNS zone should have aging enabled.'
        }
        EnabledAgingIdentical                               = @{Enable = $true
            Name                                       = 'Zone DNS aging should be identical on all DCs'
            Parameters                                 = @{Property = 'Status'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
            }
            Description                                = 'Primary DNS zone should have aging enabled, on all DNS servers.'
        }
    }
}
$DNSZonesDomain0ADEL = @{Enable = $true
    Source                      = @{Name = "DomainDNSZones should have proper FSMO Owner (0ADEL)"
        Data                             = { $IdentityDomain = "CN=Infrastructure,DC=DomainDnsZones,$(($DomainInformation).DistinguishedName)"
            $FSMORoleOwner = (Get-ADObject -Identity $IdentityDomain -Properties fSMORoleOwner -Server $Domain)
            $FSMORoleOwner }
        Details                          = [ordered] @{Area = 'Configuration'
            Category                   = 'DNS'
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ""
            Resolution                 = ''
            Resources                  = @('https://blogs.technet.microsoft.com/the_9z_by_chris_davis/2011/12/20/forestdnszones-or-domaindnszones-fsmo-says-the-role-owner-attribute-could-not-be-read/'
                'https://support.microsoft.com/en-us/help/949257/error-message-when-you-run-the-adprep-rodcprep-command-in-windows-serv'
                'https://social.technet.microsoft.com/Forums/en-US/8b4a7794-13b2-4ef0-90c8-16799e9fd529/orphaned-fsmoroleowner-entry-for-domaindnszones?forum=winserverDS')
        }
    }
    Tests                       = [ordered] @{DNSZonesDomain0ADEL = @{Enable = $true
            Name                                                             = 'DomainDNSZones should have proper FSMO Owner (0ADEL)'
            Parameters                                                       = @{ExpectedValue = '0ADEL:'
                Property                                                 = 'fSMORoleOwner'
                OperationType                                            = 'notmatch'
            }
        }
    }
}
$DNSZonesForest0ADEL = @{Enable = $true
    Source                      = @{Name = "ForestDNSZones should have proper FSMO Owner (0ADEL)"
        Data                             = { $IdentityForest = "CN=Infrastructure,DC=ForestDnsZones,$(($DomainInformation).DistinguishedName)"
            $FSMORoleOwner = (Get-ADObject -Identity $IdentityForest -Properties fSMORoleOwner -Server $Domain)
            $FSMORoleOwner }
        Requirements                     = @{IsDomainRoot = $true }
        Details                          = [ordered] @{Area = 'Configuration'
            Category                    = 'DNS'
            Severity                    = ''
            RiskLevel                   = 0
            Description                 = ""
            Resolution                  = ''
            Resources                   = @('https://blogs.technet.microsoft.com/the_9z_by_chris_davis/2011/12/20/forestdnszones-or-domaindnszones-fsmo-says-the-role-owner-attribute-could-not-be-read/'
                'https://support.microsoft.com/en-us/help/949257/error-message-when-you-run-the-adprep-rodcprep-command-in-windows-serv'
                'https://social.technet.microsoft.com/Forums/en-US/8b4a7794-13b2-4ef0-90c8-16799e9fd529/orphaned-fsmoroleowner-entry-for-domaindnszones?forum=winserverDS')
        }
    }
    Tests                       = [ordered] @{DNSZonesForest0ADEL = @{Enable = $true
            Name                                                             = 'ForestDNSZones should have proper FSMO Owner (0ADEL)'
            Parameters                                                       = @{ExpectedValue = '0ADEL:'
                Property                                                 = 'fSMORoleOwner'
                OperationType                                            = 'notmatch'
            }
        }
    }
}
$DomainFSMORoles = @{Enable = $true
    Source                  = @{Name = 'Roles availability'
        Data                         = { Test-ADRolesAvailability -Domain $Domain }
        Details                      = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                   = [ordered] @{PDCEmulator = @{Enable = $true
            Name                                                 = 'PDC Emulator Availability'
            Parameters                                           = @{ExpectedValue = $true
                Property                                         = 'PDCEmulatorAvailability'
                OperationType                                    = 'eq'
                PropertyExtendedValue                            = 'PDCEmulator'
            }
        }
        RIDMaster                                     = @{Enable = $true
            Name                               = 'RID Master Availability'
            Parameters                         = @{ExpectedValue = $true
                Property                       = 'RIDMasterAvailability'
                OperationType                  = 'eq'
                PropertyExtendedValue          = 'RIDMaster'
            }
        }
        InfrastructureMaster                          = @{Enable = $true
            Name                               = 'Infrastructure Master Availability'
            Parameters                         = @{ExpectedValue = $true
                Property                                  = 'InfrastructureMasterAvailability'
                OperationType                             = 'eq'
                PropertyExtendedValue                     = 'InfrastructureMaster'
            }
        }
    }
}
$EmptyOrganizationalUnits = @{Enable = $true
    Source                           = @{Name = "Orphaned/Empty Organizational Units"
        Data                                  = { $OrganizationalUnits = Get-ADOrganizationalUnit -Filter * -Properties distinguishedname -Server $Domain | Select-Object -ExpandProperty distinguishedname
            $WellKnownContainers = Get-ADDomain | Select-Object *Container
            $AllUsedOU = Get-ADObject -Filter "ObjectClass -eq 'user' -or ObjectClass -eq 'computer' -or ObjectClass -eq 'group' -or ObjectClass -eq 'contact'" -Server $Domain | Where-Object { ($_.DistinguishedName -notlike '*LostAndFound*') -and ($_.DistinguishedName -match 'OU=(.*)') } | ForEach-Object { $matches[0] } | Select-Object -Unique
            $OrganizationalUnits | Where-Object { ($AllUsedOU -notcontains $_) -and -not (Get-ADOrganizationalUnit -Filter * -SearchBase $_ -SearchScope 1 -Server $Domain) -and (($_ -notlike $WellKnownContainers.UsersContainer) -or ($_ -notlike $WellKnownContainers.ComputersContainer)) } }
        ExpectedOutput                        = $false
        Details                               = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = ''
            Resolution                    = ''
            Resources                     = @()
        }
    }
}
$GroupPolicyADM = @{Enable = $true
    Source                 = @{Name = "Group Policy Legacy ADM Files"
        Data                        = { Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies" -ErrorAction Stop -Recurse -Include '*.adm' | Select-Object Name, FullName, CreationTime, LastWriteTime, Attributes }
        ExpectedOutput              = $false
        Details                     = [ordered] @{Area = 'Cleanup'
            Category                      = 'Group Policy'
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = ""
            Resolution                    = ''
            Resources                     = @('https://sdmsoftware.com/group-policy-blog/tips-tricks/understanding-the-role-of-admx-and-adm-files-in-group-policy/'
                'https://social.technet.microsoft.com/Forums/en-US/bbbe04f5-218b-4526-ae67-cf82a20d49fc/deleting-adm-templates?forum=winserverGP'
                'https://gallery.technet.microsoft.com/scriptcenter/Removing-ADM-files-from-b532e3b6#content')
        }
    }
}
$GroupPolicyMissingPermissions = @{Enable = $true
    Source                                = @{Name = "Group Policy Missing Permissions"
        Data                                       = { Get-WinADGPOMissingPermissions -Domain $Domain }
        ExpectedOutput                             = $false
        Details                                    = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = "Group Policy permissions should always have Authenticated Users and Domain Computers gropup"
            Resolution                    = 'Do not remove Authenticated Users, Domain Computers from Group Policies.'
            Resources                     = @('https://secureinfra.blog/2018/12/31/most-common-mistakes-in-active-directory-and-domain-services-part-1/'
                'https://support.microsoft.com/en-us/help/3163622/ms16-072-security-update-for-group-policy-june-14-2016')
        }
    }
}
$KerberosAccountAge = @{Enable = $true
    Source                     = @{Name = "Kerberos Account Age"
        Data                            = { Get-ADUser -Identity krbtgt -Properties Created, PasswordLastSet, msDS-KeyVersionNumber -Server $Domain }
        Details                         = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                      = [ordered] @{EnabledAgingEnabled = @{Enable = $true
            Name                                                            = 'Kerberos Last Password Change Should be less than 180 days'
            Parameters                                                      = @{Property = 'PasswordLastSet'
                ExpectedValue                                       = '(Get-Date).AddDays(-180)'
                OperationType                                       = 'gt'
            }
        }
    }
}
$OrphanedForeignSecurityPrincipals = @{Enable = $true
    Source                                    = @{Name = "Orphaned Foreign Security Principals"
        Data                                           = { $AllFSP = Get-WinADUsersForeignSecurityPrincipalList -Domain $Domain
            $OrphanedObjects = $AllFSP | Where-Object { $_.TranslatedName -eq $null }
            $OrphanedObjects }
        ExpectedOutput                                 = $false
        Details                                        = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = ''
            Resolution                    = ''
            Resources                     = @()
        }
    }
}
$PasswordComplexity = @{Enable = $true
    Source                     = @{Name = 'Password Complexity Requirements'
        Data                            = { $ADModule = Import-PrivateModule PSWinDocumentation.AD
            & $ADModule { param($Domain); Get-WinADDomainDefaultPasswordPolicy -Domain $Domain } $Domain }
        Details                         = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                      = [ordered] @{ComplexityEnabled = @{Enable = $true
            Name                                                          = 'Complexity Enabled'
            Details                                                       = [ordered] @{Area = ''
                Category                                                = ''
                Severity                                                = ''
                RiskLevel                                               = 0
                Description                                             = ''
                Resolution                                              = ''
                Resources                                               = @()
            }
            Parameters                                                    = @{Property = 'Complexity Enabled'
                ExpectedValue                                     = $true
                OperationType                                     = 'eq'
            }
        }
        'LockoutDuration'                                      = @{Enable = $true
            Name                                     = 'Lockout Duration'
            Parameters                               = @{Property = 'Lockout Duration'
                ExpectedValue                     = 30
                OperationType                     = 'ge'
            }
        }
        'LockoutObservationWindow'                             = @{Enable = $true
            Name                                     = 'Lockout Observation Window'
            Parameters                               = @{Property = 'Lockout Observation Window'
                ExpectedValue                              = 30
                OperationType                              = 'ge'
            }
        }
        'LockoutThreshold'                                     = @{Enable = $true
            Name                                     = 'Lockout Threshold'
            Parameters                               = @{Property = 'Lockout Threshold'
                ExpectedValue                      = 5
                OperationType                      = 'gt'
            }
        }
        'MaxPasswordAge'                                       = @{Enable = $true
            Name                                     = 'Max Password Age'
            Parameters                               = @{Property = 'Max Password Age'
                ExpectedValue                    = 60
                OperationType                    = 'le'
            }
        }
        'MinPasswordLength'                                    = @{Enable = $true
            Name                                     = 'Min Password Length'
            Parameters                               = @{Property = 'Min Password Length'
                ExpectedValue                       = 8
                OperationType                       = 'gt'
            }
        }
        'MinPasswordAge'                                       = @{Enable = $true
            Name                                     = 'Min Password Age'
            Parameters                               = @{Property = 'Min Password Age'
                ExpectedValue                    = 1
                OperationType                    = 'le'
            }
        }
        'PasswordHistoryCount'                                 = @{Enable = $true
            Name                                     = 'Password History Count'
            Parameters                               = @{Property = 'Password History Count'
                ExpectedValue                          = 10
                OperationType                          = 'ge'
            }
        }
        'ReversibleEncryptionEnabled'                          = @{Enable = $true
            Name                                     = 'Reversible Encryption Enabled'
            Parameters                               = @{Property = 'Reversible Encryption Enabled'
                ExpectedValue                                 = $false
                OperationType                                 = 'eq'
            }
        }
    }
}
$SecurityGroupsAccountOperators = @{Enable = $true
    Source                                 = @{Name = "Groups: Account operators should be empty"
        Data                                        = { Get-ADGroupMember -Identity 'S-1-5-32-548' -Recursive -Server $Domain }
        ExpectedOutput                              = $false
        Details                                     = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = "The Account Operators group should not be used. Custom delegate instead. This group is a great 'backdoor' priv group for attackers. Microsoft even says don't use this group!"
            Resolution                    = ''
            Resources                     = @()
        }
    }
}
$SecurityGroupsSchemaAdmins = @{Enable = $true
    Source                             = @{Name = "Groups: Schema Admins should be empty"
        Data                                    = { $DomainSID = (Get-ADDomain -Server $Domain).DomainSID
            Get-ADGroupMember -Recursive -Server $Domain -Identity "$DomainSID-518" }
        Requirements                            = @{IsDomainRoot = $true }
        ExpectedOutput                          = $false
        Details                                 = [ordered] @{Area = ''
            Category                      = ''
            Severity                      = ''
            RiskLevel                     = 0
            Description                   = "Schema Admins group should be empty. If you need to manage schema you can always add user for the time of modification."
            Resolution                    = 'Keep Schema group empty.'
            Resources                     = @('https://www.stigviewer.com/stig/active_directory_forest/2016-12-19/finding/V-72835')
        }
    }
}
$SecurityUsersAcccountAdministrator = @{Enable = $true
    Source                                     = @{Name = "Users: Administrator"
        Data                                            = { $DomainSID = (Get-ADDomain -Server $Domain).DomainSID
            $User = Get-ADUser -Identity "$DomainSID-500" -Properties PasswordLastSet, LastLogonDate, servicePrincipalName -Server $Domain
            if ($User.Enabled -eq $false) {
                [PSCustomObject] @{Name = 'Administrator'
                    PasswordLastSet     = Get-Date
                }
            } else {
                [PSCustomObject] @{Name = 'Administrator'
                    PasswordLastSet     = $User.PasswordLastSet
                }
            } }
        Details                                         = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                                      = [ordered] @{PasswordLastSet = @{Enable = $true
            Name                                                                        = 'Administrator Last Password Change Should be less than 360 days ago'
            Parameters                                                                  = @{Property = 'PasswordLastSet'
                ExpectedValue                                   = '(Get-Date).AddDays(-360)'
                OperationType                                   = 'gt'
            }
            Description                                                                 = 'Administrator account should be disabled or LastPasswordChange should be less than 1 year ago.'
        }
    }
}
$SysVolDFSR = @{Enable = $true
    Source             = @{Name = "DFSR Flags"
        Data                    = { $DistinguishedName = (Get-ADDomain -Server $Domain).DistinguishedName
            $ADObject = "CN=DFSR-GlobalSettings,CN=System,$DistinguishedName"
            $Object = Get-ADObject -Identity $ADObject -Properties * -Server $Domain
            if ($Object.'msDFSR-Flags' -gt 47) {
                [PSCustomObject] @{'SysvolMode' = 'DFS-R'
                    'Flags'                     = $Object.'msDFSR-Flags'
                }
            } else {
                [PSCustomObject] @{'SysvolMode' = 'Not DFS-R'
                    'Flags'                     = $Object.'msDFSR-Flags'
                }
            } }
        Details                 = [ordered] @{Area = 'Configuration'
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = 'Checks if DFS-R is available.'
            Resolution                 = ''
            Resources                  = @('https://blogs.technet.microsoft.com/askds/2009/01/05/dfsr-sysvol-migration-faq-useful-trivia-that-may-save-your-follicles/'
                'https://dirteam.com/sander/2019/04/10/knowledgebase-in-place-upgrading-domain-controllers-to-windows-server-2019-while-still-using-ntfrs-breaks-sysvol-replication-and-dslocator/')
        }
    }
    Tests              = [ordered] @{DFSRSysvolState = @{Enable = $true
            Name                                                = 'DFSR Sysvol State'
            Parameters                                          = @{Property = 'SysvolMode'
                ExpectedValue                                   = 'DFS-R'
                OperationType                                   = 'eq'
                PropertyExtendedValue                           = 'Flags'
            }
        }
    }
}
$Trusts = @{Enable = $true
    Source         = @{Name = "Trust Availability"
        Data                = { $ADModule = Import-PrivateModule PSWinDocumentation.AD
            & $ADModule { param($Domain)
                Get-WinADDomainTrusts -Domain $Domain } -Domain $Domain }
        Details             = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = 'Verifies if trusts are available and tests for trust unconstrained TGTDelegation'
            Resolution                 = ''
            Resources                  = @('https://blogs.technet.microsoft.com/askpfeplat/2019/04/11/changes-to-ticket-granting-ticket-tgt-delegation-across-trusts-in-windows-server-askpfeplat-edition/')
        }
    }
    Tests          = [ordered] @{TrustsConnectivity = @{Enable = $true
            Name                                               = 'Trust status verification'
            Parameters                                         = @{OverwriteName = { "Trust status verification | Source $Domain, Target $($_.'Trust Target'), Direction $($_.'Trust Direction')" }
                Property                                                = 'Trust Status'
                ExpectedValue                                           = 'OK'
                OperationType                                           = 'eq'
            }
        }
        TrustsUnconstrainedDelegation               = @{Enable = $true
            Name                                      = 'Trust unconstrained TGTDelegation'
            Parameters                                = @{OverwriteName = { "Trust unconstrained TGTDelegation | Source $Domain, Target $($_.'Trust Target'), Direction $($_.'Trust Direction')" }
                WhereObject                                        = { $($_.'Trust Direction' -eq 'BiDirectional' -or $_.'Trust Direction' -eq 'InBound') }
                Property                                           = 'TGTDelegation'
                ExpectedValue                                      = $True
                OperationType                                      = 'eq'
            }
        }
    }
}
$WellKnownFolders = @{Enable = $true
    Source                   = @{Name = 'Well known folders'
        Data                          = { $DomainInformation = Get-ADDomain -Server $Domain
            $WellKnownFolders = $DomainInformation | Select-Object -Property UsersContainer, ComputersContainer, DomainControllersContainer, DeletedObjectsContainer, SystemsContainer, LostAndFoundContainer, QuotasContainer, ForeignSecurityPrincipalsContainer
            $CurrentWellKnownFolders = [ordered] @{ }
            $DomainDistinguishedName = $DomainInformation.DistinguishedName
            $DefaultWellKnownFolders = [ordered] @{UsersContainer = "CN=Users,$DomainDistinguishedName"
                ComputersContainer                                = "CN=Computers,$DomainDistinguishedName"
                DomainControllersContainer                        = "OU=Domain Controllers,$DomainDistinguishedName"
                DeletedObjectsContainer                           = "CN=Deleted Objects,$DomainDistinguishedName"
                SystemsContainer                                  = "CN=System,$DomainDistinguishedName"
                LostAndFoundContainer                             = "CN=LostAndFound,$DomainDistinguishedName"
                QuotasContainer                                   = "CN=NTDS Quotas,$DomainDistinguishedName"
                ForeignSecurityPrincipalsContainer                = "CN=ForeignSecurityPrincipals,$DomainDistinguishedName"
            }
            foreach ($_ in $WellKnownFolders.PSObject.Properties.Name) {
                $CurrentWellKnownFolders[$_] = $DomainInformation.$_
                $CurrentWellKnownFolders[$_] = $DomainInformation.$_
            }
            Compare-MultipleObjects -Object @($DefaultWellKnownFolders, $CurrentWellKnownFolders) -SkipProperties }
        Details                       = [ordered] @{Area = ''
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = 'Verifies whether well-known folders are at their defaults or not.'
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                    = [ordered] @{UsersContainer = @{Enable = $true
            Name                                                     = "Users Container shouldn't be at default"
            Parameters                                               = @{WhereObject = { $_.Name -eq 'UsersContainer' }
                ExpectedValue                                     = $false
                Property                                          = 'Status'
                OperationType                                     = 'eq'
                PropertyExtendedValue                             = '1'
            }
        }
        ComputersContainer                                = @{Enable = $true
            Name                                      = "Computers Container shouldn't be at default"
            Parameters                                = @{WhereObject = { $_.Name -eq 'ComputersContainer' }
                ExpectedValue                         = $false
                Property                              = 'Status'
                OperationType                         = 'eq'
                PropertyExtendedValue                 = '1'
            }
        }
        DomainControllersContainer                        = @{Enable = $true
            Name                                      = "Domain Controllers Container should be at default location"
            Parameters                                = @{WhereObject = { $_.Name -eq 'DomainControllersContainer' }
                ExpectedValue                                 = $true
                Property                                      = 'Status'
                OperationType                                 = 'eq'
                PropertyExtendedValue                         = '1'
            }
        }
        DeletedObjectsContainer                           = @{Enable = $true
            Name                                      = "Deleted Objects Container should be at default location"
            Parameters                                = @{WhereObject = { $_.Name -eq 'DeletedObjectsContainer' }
                ExpectedValue                              = $true
                Property                                   = 'Status'
                OperationType                              = 'eq'
                PropertyExtendedValue                      = '1'
            }
        }
        SystemsContainer                                  = @{Enable = $true
            Name                                      = "Systems Container should be at default location"
            Parameters                                = @{WhereObject = { $_.Name -eq 'SystemsContainer' }
                ExpectedValue                       = $true
                Property                            = 'Status'
                OperationType                       = 'eq'
                PropertyExtendedValue               = '1'
            }
        }
        LostAndFoundContainer                             = @{Enable = $true
            Name                                      = "Lost And Found Container should be at default location"
            Parameters                                = @{WhereObject = { $_.Name -eq 'LostAndFoundContainer' }
                ExpectedValue                            = $true
                Property                                 = 'Status'
                OperationType                            = 'eq'
                PropertyExtendedValue                    = '1'
            }
        }
        QuotasContainer                                   = @{Enable = $true
            Name                                      = "Quotas Container shouldn be at default location"
            Parameters                                = @{WhereObject = { $_.Name -eq 'QuotasContainer' }
                ExpectedValue                      = $true
                Property                           = 'Status'
                OperationType                      = 'eq'
                PropertyExtendedValue              = '1'
            }
        }
        ForeignSecurityPrincipalsContainer                = @{Enable = $true
            Name                                      = "Foreign Security Principals Container should be at default location"
            Parameters                                = @{WhereObject = { $_.Name -eq 'ForeignSecurityPrincipalsContainer' }
                ExpectedValue                                         = $true
                Property                                              = 'Status'
                OperationType                                         = 'eq'
                PropertyExtendedValue                                 = '1'
            }
        }
    }
}
$DFS = @{Enable = $true
    Source      = @{Name = "SYSVOL/DFS Verification"
        Data             = { Get-WinADDFSHealth -Domains $Domain -DomainControllers $DomainController }
        Parameters       = @{EventDays = 3 }
        Details          = [ordered] @{Area = 'Configuration'
            Category                   = 'DFS'
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ""
            Resolution                 = ''
            Resources                  = @('https://support.microsoft.com/en-us/help/2218556/how-to-force-an-authoritative-and-non-authoritative-synchronization-fo'
                'https://www.itprotoday.com/windows-78/fixing-broken-sysvol-replication'
                'https://www.brisk-it.net/when-dfs-replication-goes-wrong-and-how-to-fix-it/'
                'https://gallery.technet.microsoft.com/scriptcenter/AD-DFS-Replication-Auto-812a88bc'
                'https://www.reddit.com/r/sysadmin/comments/7gey4k/resuming_dfs_replication_after_4_years_of_no/'
                'https://kimconnect.com/fix-dfs-replication-problems/'
                'https://community.spiceworks.com/topic/2205945-repairing-broken-dfs-replication'
                'https://support.microsoft.com/en-us/help/2958414/dfs-replication-how-to-troubleshoot-missing-sysvol-and-netlogon-shares'
                'https://noobient.com/2013/11/11/fixing-sysvol-replication-on-windows-server-2012/'
                'https://jackstromberg.com/2014/07/sysvol-and-group-policy-out-of-sync-on-server-2012-r2-dcs-using-dfsr/')
        }
    }
    Tests       = [ordered] @{Status = @{Enable = $true
            Name                                = 'DFS should be Healthy'
            Parameters                          = @{ExpectedValue = $true
                Property                                    = 'Status'
                OperationType                               = 'eq'
            }
        }
        ReplicationState             = @{Enable = $true
            Name                          = 'Replication State should be NORMAL'
            Parameters                    = @{ExpectedValue = 'Normal'
                Property                              = 'ReplicationState'
                OperationType                         = 'eq'
            }
        }
        CentralRepository            = @{Enable = $true
            Name                          = 'Central Repository for GPO for Domain should be available'
            Parameters                    = @{ExpectedValue = $true
                Property                               = 'CentralRepository'
                OperationType                          = 'eq'
            }
        }
        CentralRepositoryDC          = @{Enable = $true
            Name                          = 'Central Repository for GPO for DC should be available'
            Parameters                    = @{ExpectedValue = $true
                Property                                 = 'CentralRepositoryDC'
                OperationType                            = 'eq'
            }
        }
        IdenticalCount               = @{Enable = $true
            Name                          = 'GPO Count should match folder count'
            Parameters                    = @{ExpectedValue = $true
                Property                            = 'IdenticalCount'
                OperationType                       = 'eq'
            }
        }
        MemberReference              = @{Enable = $true
            Name                          = 'MemberReference should return TRUE'
            Parameters                    = @{ExpectedValue = $true
                Property                             = 'MemberReference'
                OperationType                        = 'eq'
            }
        }
        DFSErrors                    = @{Enable = $true
            Name                          = 'DFSErrors should be 0'
            Parameters                    = @{ExpectedValue = 0
                Property                       = 'DFSErrors'
                OperationType                  = 'eq'
            }
        }
        DFSLocalSetting              = @{Enable = $true
            Name                          = 'DFSLocalSetting should be TRUE'
            Parameters                    = @{ExpectedValue = $true
                Property                             = 'DFSLocalSetting'
                OperationType                        = 'eq'
            }
        }
        DomainSystemVolume           = @{Enable = $true
            Name                          = 'DomainSystemVolume should be TRUE'
            Parameters                    = @{ExpectedValue = $true
                Property                                = 'DomainSystemVolume'
                OperationType                           = 'eq'
            }
        }
        SYSVOLSubscription           = @{Enable = $true
            Name                          = 'SYSVOLSubscription should be TRUE'
            Parameters                    = @{ExpectedValue = $true
                Property                                = 'SYSVOLSubscription'
                OperationType                           = 'eq'
            }
        }
        DFSRAutoRecovery             = @{Enable = $true
            Name                          = 'DFSR AutoRecovery should be enabled (not stopped)'
            Parameters                    = @{Property = 'StopReplicationOnAutoRecovery'
                ExpectedValue                    = $false
                OperationType                    = 'eq'
            }
            Details                       = [ordered] @{Area = ''
                Description                            = ''
                Resolution                             = ''
                RiskLevel                              = 10
                Resources                              = @('https://secureinfra.blog/2019/04/30/field-notes-a-quick-tip-on-dfsr-automatic-recovery-while-you-prepare-for-an-ad-domain-upgrade/'
                    'https://richardjgreen.net/active-directory-dfs-r-auto-recovery/')
            }
        }
    }
}
$Diagnostics = @{Enable = $true
    Source              = @{Name = 'Diagnostics (DCIAG)'
        Data                     = { Test-ADDomainController -ComputerName $DomainController -WarningAction SilentlyContinue }
        Details                  = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @('https://social.technet.microsoft.com/Forums/en-US/b48ee073-eb71-4852-8f56-ecf6f76b3fff/how-could-i-change-result-of-dcdiag-language-to-english-?forum=winserver8gen')
        }
    }
    Tests               = [ordered] @{Connectivity = @{Enable = $true
            Name                                              = 'DCDiag Connectivity'
            Parameters                                        = @{WhereObject = { $_.Test -eq 'Connectivity' }
                Property                                        = 'Result'
                ExpectedValue                                   = $true
                OperationType                                   = 'eq'
            }
        }
        Advertising                                = @{Enable = $true
            Name                                    = 'DCDiag Advertising'
            Parameters                              = @{WhereObject = { $_.Test -eq 'Advertising' }
                Property                       = 'Result'
                ExpectedValue                  = $true
                OperationType                  = 'eq'
            }
        }
        CheckSecurityError                         = @{Enable = $true
            Name                                    = 'DCDiag CheckSecurityError'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CheckSecurityError' }
                Property                              = 'Result'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
            }
        }
        CutoffServers                              = @{Enable = $true
            Name                                    = 'DCDiag CutoffServers'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CutoffServers' }
                Property                         = 'Result'
                ExpectedValue                    = $true
                OperationType                    = 'eq'
            }
        }
        FrsEvent                                   = @{Enable = $true
            Name                                    = 'DCDiag FrsEvent'
            Parameters                              = @{WhereObject = { $_.Test -eq 'FrsEvent' }
                Property                    = 'Result'
                ExpectedValue               = $true
                OperationType               = 'eq'
            }
        }
        DFSREvent                                  = @{Enable = $true
            Name                                    = 'DCDiag DFSREvent'
            Parameters                              = @{WhereObject = { $_.Test -eq 'DFSREvent' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        SysVolCheck                                = @{Enable = $true
            Name                                    = 'DCDiag SysVolCheck'
            Parameters                              = @{WhereObject = { $_.Test -eq 'SysVolCheck' }
                Property                       = 'Result'
                ExpectedValue                  = $true
                OperationType                  = 'eq'
            }
        }
        FrsSysVol                                  = @{Enable = $true
            Name                                    = 'DCDiag FrsSysVol'
            Parameters                              = @{WhereObject = { $_.Test -eq 'FrsSysVol' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        KccEvent                                   = @{Enable = $true
            Name                                    = 'DCDiag KccEvent'
            Parameters                              = @{WhereObject = { $_.Test -eq 'KccEvent' }
                Property                    = 'Result'
                ExpectedValue               = $true
                OperationType               = 'eq'
            }
        }
        KnowsOfRoleHolders                         = @{Enable = $true
            Name                                    = 'DCDiag KnowsOfRoleHolders'
            Parameters                              = @{WhereObject = { $_.Test -eq 'KnowsOfRoleHolders' }
                Property                              = 'Result'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
            }
        }
        MachineAccount                             = @{Enable = $true
            Name                                    = 'DCDiag MachineAccount'
            Parameters                              = @{WhereObject = { $_.Test -eq 'MachineAccount' }
                Property                          = 'Result'
                ExpectedValue                     = $true
                OperationType                     = 'eq'
            }
        }
        NCSecDesc                                  = @{Enable = $true
            Name                                    = 'DCDiag NCSecDesc'
            Parameters                              = @{WhereObject = { $_.Test -eq 'NCSecDesc' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        NetLogons                                  = @{Enable = $true
            Name                                    = 'DCDiag NetLogons'
            Parameters                              = @{WhereObject = { $_.Test -eq 'NetLogons' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        ObjectsReplicated                          = @{Enable = $true
            Name                                    = 'DCDiag ObjectsReplicated'
            Parameters                              = @{WhereObject = { $_.Test -eq 'ObjectsReplicated' }
                Property                             = 'Result'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
            }
        }
        Replications                               = @{Enable = $true
            Name                                    = 'DCDiag Replications'
            Parameters                              = @{WhereObject = { $_.Test -eq 'Replications' }
                Property                        = 'Result'
                ExpectedValue                   = $true
                OperationType                   = 'eq'
            }
        }
        RidManager                                 = @{Enable = $true
            Name                                    = 'DCDiag RidManager'
            Parameters                              = @{WhereObject = { $_.Test -eq 'RidManager' }
                Property                      = 'Result'
                ExpectedValue                 = $true
                OperationType                 = 'eq'
            }
        }
        Services                                   = @{Enable = $true
            Name                                    = 'DCDiag Services'
            Parameters                              = @{WhereObject = { $_.Test -eq 'Services' }
                Property                    = 'Result'
                ExpectedValue               = $true
                OperationType               = 'eq'
            }
        }
        SystemLog                                  = @{Enable = $true
            Name                                    = 'DCDiag SystemLog'
            Parameters                              = @{WhereObject = { $_.Test -eq 'SystemLog' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        Topology                                   = @{Enable = $true
            Name                                    = 'DCDiag Topology'
            Parameters                              = @{WhereObject = { $_.Test -eq 'Topology' }
                Property                    = 'Result'
                ExpectedValue               = $true
                OperationType               = 'eq'
            }
        }
        VerifyEnterpriseReferences                 = @{Enable = $true
            Name                                    = 'DCDiag VerifyEnterpriseReferences'
            Parameters                              = @{WhereObject = { $_.Test -eq 'VerifyEnterpriseReferences' }
                Property                                      = 'Result'
                ExpectedValue                                 = $true
                OperationType                                 = 'eq'
            }
        }
        VerifyReferences                           = @{Enable = $true
            Name                                    = 'DCDiag VerifyReferences'
            Parameters                              = @{WhereObject = { $_.Test -eq 'VerifyReferences' }
                Property                            = 'Result'
                ExpectedValue                       = $true
                OperationType                       = 'eq'
            }
        }
        VerifyReplicas                             = @{Enable = $true
            Name                                    = 'DCDiag VerifyReplicas'
            Parameters                              = @{WhereObject = { $_.Test -eq 'VerifyReplicas' }
                Property                          = 'Result'
                ExpectedValue                     = $true
                OperationType                     = 'eq'
            }
        }
        DNS                                        = @{Enable = $true
            Name                                    = 'DCDiag DNS'
            Parameters                              = @{WhereObject = { $_.Test -eq 'DNS' -and $_.Target -ne $Domain }
                Property               = 'Result'
                ExpectedValue          = $true
                OperationType          = 'eq'
            }
        }
        ForestDnsZonesCheckSDRefDom                = @{Enable = $true
            Name                                    = 'DCDiag ForestDnsZones CheckSDRefDom'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CheckSDRefDom' -and $_.Target -eq 'ForestDnsZones' }
                Property                                       = 'Result'
                ExpectedValue                                  = $true
                OperationType                                  = 'eq'
            }
        }
        ForestDnsZonesCrossRefValidation           = @{Enable = $true
            Name                                    = 'DCDiag ForestDnsZones CrossRefValidation'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CrossRefValidation' -and $_.Target -eq 'ForestDnsZones' }
                Property                                            = 'Result'
                ExpectedValue                                       = $true
                OperationType                                       = 'eq'
            }
        }
        DomainDnsZonesCheckSDRefDom                = @{Enable = $true
            Name                                    = 'DCDiag DomainDnsZones CheckSDRefDom'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CheckSDRefDom' -and $_.Target -eq 'DomainDnsZones' }
                Property                                       = 'Result'
                ExpectedValue                                  = $true
                OperationType                                  = 'eq'
            }
        }
        DomainDnsZonesCrossRefValidation           = @{Enable = $true
            Name                                    = 'DCDiag DomainDnsZones CrossRefValidation'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CrossRefValidation' -and $_.Target -eq 'DomainDnsZones' }
                Property                                            = 'Result'
                ExpectedValue                                       = $true
                OperationType                                       = 'eq'
            }
        }
        SchemaCheckSDRefDom                        = @{Enable = $true
            Name                                    = 'DCDiag Schema CheckSDRefDom'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CheckSDRefDom' -and $_.Target -eq 'Schema' }
                Property                               = 'Result'
                ExpectedValue                          = $true
                OperationType                          = 'eq'
            }
        }
        SchemaCrossRefValidation                   = @{Enable = $true
            Name                                    = 'DCDiag Schema CrossRefValidation'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CrossRefValidation' -and $_.Target -eq 'Schema' }
                Property                                    = 'Result'
                ExpectedValue                               = $true
                OperationType                               = 'eq'
            }
        }
        ConfigurationCheckSDRefDom                 = @{Enable = $true
            Name                                    = 'DCDiag Configuration CheckSDRefDom'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CheckSDRefDom' -and $_.Target -eq 'Configuration' }
                Property                                      = 'Result'
                ExpectedValue                                 = $true
                OperationType                                 = 'eq'
            }
        }
        ConfigurationCrossRefValidation            = @{Enable = $true
            Name                                    = 'DCDiag Configuration CrossRefValidation'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CrossRefValidation' -and $_.Target -eq 'Configuration' }
                Property                                           = 'Result'
                ExpectedValue                                      = $true
                OperationType                                      = 'eq'
            }
        }
        NetbiosCheckSDRefDom                       = @{Enable = $true
            Name                                    = 'DCDiag NETBIOS CheckSDRefDom'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CheckSDRefDom' -and ($_.Target -ne 'Configuration' -and $_.Target -ne 'ForestDnsZones' -and $_.Target -ne 'DomainDnsZones' -and $_.Target -ne 'Schema') }
                Property                                = 'Result'
                ExpectedValue                           = $true
                OperationType                           = 'eq'
            }
        }
        NetbiosCrossRefValidation                  = @{Enable = $true
            Name                                    = 'DCDiag NETBIOS CrossRefValidation'
            Parameters                              = @{WhereObject = { $_.Test -eq 'CrossRefValidation' -and ($_.Target -ne 'Configuration' -and $_.Target -ne 'ForestDnsZones' -and $_.Target -ne 'DomainDnsZones' -and $_.Target -ne 'Schema') }
                Property                                     = 'Result'
                ExpectedValue                                = $true
                OperationType                                = 'eq'
            }
        }
        DNSDomain                                  = @{Enable = $true
            Name                                    = 'DCDiag DNS'
            Parameters                              = @{WhereObject = { $_.Test -eq 'DNS' -and $_.Target -eq $Domain }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        LocatorCheck                               = @{Enable = $true
            Name                                    = 'DCDiag LocatorCheck'
            Parameters                              = @{WhereObject = { $_.Test -eq 'LocatorCheck' }
                Property                        = 'Result'
                ExpectedValue                   = $true
                OperationType                   = 'eq'
            }
        }
        FsmoCheck                                  = @{Enable = $true
            Name                                    = 'DCDiag FsmoCheck'
            Parameters                              = @{WhereObject = { $_.Test -eq 'FsmoCheck' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        Intersite                                  = @{Enable = $true
            Name                                    = 'DCDiag Intersite'
            Parameters                              = @{WhereObject = { $_.Test -eq 'Intersite' }
                Property                     = 'Result'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
    }
}
$DiskSpace = @{Enable = $true
    Source            = @{Name = 'Disk Free'
        Data                   = { Get-ComputerDiskLogical -ComputerName $DomainController -OnlyLocalDisk -WarningAction SilentlyContinue }
        Details                = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests             = [ordered] @{FreeSpace = @{Enable = $true
            Name                                         = 'Free Space in GB'
            Parameters                                   = @{Property   = 'FreeSpace'
                PropertyExtendedValue = 'FreeSpace'
                ExpectedValue         = 10
                OperationType         = 'gt'
            }
        }
        FreePercent                           = @{Enable = $true
            Name                             = 'Free Space Percent'
            Parameters                       = @{Property = 'FreePercent'
                PropertyExtendedValue       = 'FreePercent'
                ExpectedValue               = 10
                OperationType               = 'gt'
            }
        }
    }
}
$DNSNameServers = @{Enable = $true
    Source                 = @{Name = "Name servers for primary domain zone"
        Data                        = { Test-DNSNameServers -Domain $Domain -DomainController $DomainController }
        Details                     = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                  = [ordered] @{DnsNameServersIdentical = @{Enable = $true
            Name                                                            = 'DNS Name servers for primary zone are identical'
            Parameters                                                      = @{Property = 'Status'
                ExpectedValue                                           = $True
                OperationType                                           = 'eq'
                PropertyExtendedValue                                   = 'Comment'
            }
            Description                                                     = 'DNS Name servers for primary zone should be equal to Domain Controllers for a Domain.'
        }
    }
}
$DNSResolveExternal = @{Enable = $true
    Source                     = @{Name = "Resolves external DNS queries"
        Data                            = { $Output = Invoke-Command -ComputerName $DomainController -ErrorAction Stop { Resolve-DnsName -Name 'evotec.xyz' -ErrorAction SilentlyContinue }
            $Output }
        Details                         = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                      = [ordered] @{ResolveDNSExternal = @{Enable = $true
            Name                                                           = 'Should resolve External DNS'
            Parameters                                                     = @{Property = 'IPAddress'
                ExpectedValue                                      = '37.59.176.139'
                OperationType                                      = 'eq'
            }
            Description                                                    = 'DNS should resolve external queries properly.'
        }
    }
}
$DNSResolveInternal = @{Enable = $true
    Source                     = @{Name = "Resolves internal DNS queries"
        Data                            = { $Output = Invoke-Command -ComputerName $DomainController -ErrorAction Stop { param([string] $DomainController)
                $AllDomainControllers = Get-ADDomainController -Identity $DomainController -Server $DomainController
                $IPs = $AllDomainControllers.IPv4Address | Sort-Object
                $Output = Resolve-DnsName -Name $DomainController -ErrorAction SilentlyContinue
                @{'Result'        = 'IP Comparison'
                    'Status'      = if ($null -eq (Compare-Object -ReferenceObject $IPs -DifferenceObject ($Output.IP4Address | Sort-Object))) { $true } else { $false }
                    'IPAddresses' = $Output.IP4Address
                } } -ArgumentList $DomainController
            $Output }
        Details                         = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                      = [ordered] @{ResolveDNSInternal = @{Enable = $true
            Name                                                           = 'Should resolve Internal DNS'
            Parameters                                                     = @{Property = 'Status'
                ExpectedValue                                      = $true
                OperationType                                      = 'eq'
                PropertyExtendedValue                              = 'IPAddresses'
            }
            Description                                                    = 'DNS should resolve internal domains correctly.'
        }
    }
}
$EventLogs = @{Enable = $true
    Source            = @{Name = "Event Logs"
        Data                   = { Get-EventsInformation -LogName 'Application', 'System', 'Security', 'Microsoft-Windows-PowerShell/Operational' -Machine $DomainController -WarningAction SilentlyContinue }
        Details                = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests             = [ordered] @{ApplicationLogMode   = @{Enable = $true
            Name                                                  = 'Application Log mode is set to AutoBackup'
            Parameters                                            = @{WhereObject = { $_.LogName -eq 'Application' }
                Property                                              = 'LogMode'
                ExpectedValue                                         = 'AutoBackup'
                OperationType                                         = 'eq'
            }
        }
        ApplicationLogFull                               = @{Enable = $true
            Name                                                    = 'Application log is not full'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Application' }
                Property                              = 'IsLogFull'
                ExpectedValue                         = $false
                OperationType                         = 'eq'
            }
        }
        PowershellLogMode                                = @{Enable = $true
            Name                                                    = 'PowerShell Log mode is set to AutoBackup'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Microsoft-Windows-PowerShell/Operational' }
                Property                             = 'LogMode'
                ExpectedValue                        = 'AutoBackup'
                OperationType                        = 'eq'
            }
        }
        PowerShellLogFull                                = @{Enable = $true
            Name                                                    = 'PowerShell log is not full'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Microsoft-Windows-PowerShell/Operational' }
                Property                             = 'IsLogFull'
                ExpectedValue                        = $false
                OperationType                        = 'eq'
            }
        }
        SystemLogMode                                    = @{Enable = $true
            Name                                                    = 'System Log mode is set to AutoBackup'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'System' }
                Property                         = 'LogMode'
                ExpectedValue                    = 'AutoBackup'
                OperationType                    = 'eq'
            }
        }
        SystemLogFull                                    = @{Enable = $true
            Name                                                    = 'System log is not full'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'System' }
                Property                         = 'IsLogFull'
                ExpectedValue                    = $false
                OperationType                    = 'eq'
            }
        }
        SecurityLogMode                                  = @{Enable = $true
            Name                                                    = 'Security Log mode is set to AutoBackup'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' }
                Property                           = 'LogMode'
                ExpectedValue                      = 'AutoBackup'
                OperationType                      = 'eq'
            }
        }
        SecurityLogFull                                  = @{Enable = $true
            Name                                                    = 'Security log is not full'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' }
                Property                           = 'IsLogFull'
                ExpectedValue                      = $false
                OperationType                      = 'eq'
            }
        }
        SecurityMaximumLogSize                           = @{Enable = $true
            Name                                                    = 'Security Log Maximum Size smaller then 4GB'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' }
                Property                                  = 'FileSizeMaximumMB'
                ExpectedValue                             = 4000
                OperationType                             = 'le'
            }
        }
        SecurityCurrentLogSize                           = @{Enable = $true
            Name                                                    = 'Security Log Current Size smaller then 4GB'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' }
                Property                                  = 'FileSizeCurrentMB'
                ExpectedValue                             = 4000
                OperationType                             = 'le'
            }
        }
        SecurityPermissionsDefaultNetworkService         = @{Enable = $true
            Name                                                    = 'Security Log has NT AUTHORITY\NETWORK SERVICE with AccessAllowed'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' -and $_.SecurityDescriptorDiscretionaryAcl -contains 'NT AUTHORITY\NETWORK SERVICE: AccessAllowed (ListDirectory)' }
                ExpectedCount                                               = 1
                OperationType                                               = 'eq'
            }
        }
        SecurityPermissionsDefaultSYSTEM                 = @{Enable = $true
            Name                                                    = 'Security Log has NT AUTHORITY\SYSTEM with AccessAllowed'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' -and $_.SecurityDescriptorDiscretionaryAcl -contains 'NT AUTHORITY\SYSTEM: AccessAllowed (ChangePermissions, CreateDirectories, Delete, GenericExecute, ListDirectory, ReadPermissions, TakeOwnership)' }
                ExpectedCount                                       = 1
                OperationType                                       = 'eq'
            }
        }
        SecurityPermissionsNDefaultBuiltinAdministrators = @{Enable = $true
            Name                                                    = 'Security Log has BUILTIN\Administrators with AccessAllowed'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' -and $_.SecurityDescriptorDiscretionaryAcl -contains 'BUILTIN\Administrators: AccessAllowed (CreateDirectories, ListDirectory)' }
                ExpectedCount                                                       = 1
                OperationType                                                       = 'eq'
            }
        }
        SecurityPermissionsDefaultBuiltinEventLogReaders = @{Enable = $true
            Name                                                    = 'Security Log has BUILTIN\Event Log Readers with AccessAllowed'
            Parameters                                              = @{WhereObject = { $_.LogName -eq 'Security' -and $_.SecurityDescriptorDiscretionaryAcl -contains 'BUILTIN\Event Log Readers: AccessAllowed (ListDirectory)' }
                ExpectedCount                                                       = 1
                OperationType                                                       = 'eq'
            }
        }
    }
}
$FileSystem = @{Enable = $true
    Source             = @{Name = "FileSystem"
        Data                    = { Get-PSRegistry -RegistryPath 'HKLM\SYSTEM\CurrentControlSet\Control\FileSystem' -ComputerName $DomainController }
        Details                 = [ordered] @{Type = 'Security'
            Area                          = ''
            Description                   = ''
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @('')
        }
        Requirements            = @{CommandAvailable = 'Get-WinADLMSettings' }
        ExpectedOutput          = $true
    }
    Tests              = [ordered] @{NtfsDisable8dot3NameCreation = @{Enable = $true
            Name                                                             = 'NtfsDisable8dot3NameCreation'
            Parameters                                                       = @{Property = 'NtfsDisable8dot3NameCreation'
                ExpectedValue                                                = 0
                OperationType                                                = 'gt'
            }
            Details                                                          = [ordered] @{Area = ''
                Description                                                        = ''
                Resolution                                                         = ''
                RiskLevel                                                          = 10
                Resources                                                          = @('https://guyrleech.wordpress.com/2014/04/15/ntfs-8-3-short-names-solving-the-issues/'
                    'https://blogs.technet.microsoft.com/josebda/2012/11/13/windows-server-2012-file-server-tip-disable-8-3-naming-and-strip-those-short-names-too/'
                    'https://support.microsoft.com/en-us/help/121007/how-to-disable-8-3-file-name-creation-on-ntfs-partitions')
            }
        }
    }
}
$GroupPolicySYSVOL = @{Enable = $true
    Source                    = @{Name = "Group Policy SYSVOL Verification"
        Data                           = { Get-WinADGPOSysvolFolders -Domain $Domain -IncludeDomainControllers $DomainController | Where-Object { $_.SysvolStatus -ne 'Exists' } }
        Details                        = [ordered] @{Area = ''
            Description                   = ''
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @()
        }
        ExpectedOutput                 = $false
    }
}
$Information = @{Enable = $true
    Source              = @{Name = "Domain Controller Information"
        Data                     = { Get-ADDomainController -Server $DomainController }
        Details                  = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests               = [ordered] @{IsEnabled = @{Enable = $true
            Name                                           = 'Is Enabled'
            Parameters                                     = @{Property = 'Enabled'
                ExpectedValue                             = $True
                OperationType                             = 'eq'
            }
        }
        IsGlobalCatalog                         = @{Enable = $true
            Name                             = 'Is Global Catalog'
            Parameters                       = @{Property = 'IsGlobalCatalog'
                ExpectedValue                   = $True
                OperationType                   = 'eq'
            }
        }
    }
}
$LanManagerSettings = @{Enable = $true
    Source                     = @{Name = "Lan Manager Settings"
        Data                            = { Get-WinADLMSettings -DomainController $DomainController }
        Details                         = [ordered] @{Area = ''
            Description                   = ''
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @('https://adsecurity.org/?p=3377')
        }
        Requirements                    = @{CommandAvailable = 'Get-WinADLMSettings' }
        ExpectedOutput                  = $true
    }
    Tests                      = [ordered] @{Level = @{Enable = $true
            Name                                              = 'LM Level'
            Parameters                                        = @{Property = 'Level'
                ExpectedValue                         = 5
                OperationType                         = 'eq'
            }
            Details                                           = [ordered] @{Area = ''
                Description                                 = ''
                Resolution                                  = ''
                RiskLevel                                   = 10
                Resources                                   = @()
            }
        }
        AuditBaseObjects                           = @{Enable = $true
            Name                             = 'Audit Base Objects'
            Parameters                       = @{Property = 'AuditBaseObjects'
                ExpectedValue                    = $false
                OperationType                    = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                            = ''
                Resolution                             = ''
                RiskLevel                              = 10
                Resources                              = @('https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpac/262a2bed-93d4-4c04-abec-cf06e9ec72fd')
            }
        }
        CrashOnAuditFail                           = @{Enable = $true
            Name                             = 'Crash On Audit Fail'
            Parameters                       = @{Property = 'CrashOnAuditFail'
                ExpectedValue                    = 0
                OperationType                    = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                            = ''
                Resolution                             = ''
                RiskLevel                              = 10
                Resources                              = @('http://systemmanager.ru/win2k_regestry.en/46686.htm')
            }
        }
        EveryoneIncludesAnonymous                  = @{Enable = $true
            Name                             = 'Everyone Includes Anonymous'
            Parameters                       = @{Property = 'EveryoneIncludesAnonymous'
                ExpectedValue                             = $false
                OperationType                             = 'eq'
            }
            Details                          = [ordered] @{Title = 'Disable and Enforce the Setting "Network access: Let Everyone permissions apply to anonymous users"'
                Area                                             = ''
                Description                                      = 'This setting helps to prevent an unauthorized user could from anonymously listing account names and shared resources and use using the information to attempt to guess passwords, perform social engineering attacks, or launch DoS attacks.'
                Resolution                                       = ''
                RiskLevel                                        = 10
                Resources                                        = @('https://www.stigviewer.com/stig/windows_7/2014-04-02/finding/V-3377')
            }
        }
        SecureBoot                                 = @{Enable = $true
            Name                             = 'Secure Boot'
            Parameters                       = @{Property = 'SecureBoot'
                ExpectedValue              = $true
                OperationType              = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                      = ''
                Resolution                       = ''
                RiskLevel                        = 10
                Resources                        = @()
            }
        }
        LSAProtectionCredentials                   = @{Enable = $true
            Name                             = 'LSAProtectionCredentials'
            Parameters                       = @{Property = 'LSAProtectionCredentials'
                ExpectedValue                            = $true
                OperationType                            = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                                    = ''
                Resolution                                     = ''
                RiskLevel                                      = 10
                Resources                                      = @()
            }
        }
        LimitBlankPasswordUse                      = @{Enable = $true
            Name                             = 'LimitBlankPasswordUse'
            Parameters                       = @{Property = 'LimitBlankPasswordUse'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                                 = ''
                Resolution                                  = ''
                RiskLevel                                   = 10
                Resources                                   = @()
            }
        }
        NoLmHash                                   = @{Enable = $true
            Name                             = 'NoLmHash'
            Parameters                       = @{Property = 'NoLmHash'
                ExpectedValue            = $true
                OperationType            = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                    = ''
                Resolution                     = ''
                RiskLevel                      = 10
                Resources                      = @()
            }
        }
        DisableDomainCreds                         = @{Enable = $true
            Name                             = 'DisableDomainCreds'
            Parameters                       = @{Property = 'DisableDomainCreds'
                ExpectedValue                      = $false
                OperationType                      = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                              = ''
                Resolution                               = ''
                RiskLevel                                = 10
                Resources                                = @('https://www.stigviewer.com/stig/windows_8/2014-01-07/finding/V-3376')
            }
        }
        ForceGuest                                 = @{Enable = $true
            Name                             = 'ForceGuest'
            Parameters                       = @{Property = 'ForceGuest'
                ExpectedValue              = $false
                OperationType              = 'eq'
            }
            Details                          = [ordered] @{Area = ''
                Description                      = ''
                Resolution                       = ''
                RiskLevel                        = 10
                Resources                        = @()
            }
        }
    }
}
$LanManServer = @{Enable = $true
    Source               = @{Name = "Lan Man Server"
        Data                      = { Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanManServer\Parameters' -ComputerName $DomainController }
        Details                   = [ordered] @{Area = ''
            Description                   = 'Lan Man Server'
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @()
        }
        Requirements              = @{CommandAvailable = 'Get-PSRegistry' }
        ExpectedOutput            = $true
    }
    Tests                = [ordered] @{EnableForcedLogoff = @{Enable = $true
            Name                                                     = 'Enable Forced Logoff'
            Parameters                                               = @{Property = 'EnableForcedLogoff'
                ExpectedValue                                      = 1
                OperationType                                      = 'eq'
            }
            Details                                                  = [ordered] @{Area = ''
                Description                                              = 'Users are not forcibly disconnected when logon hours expire.'
                Resolution                                               = ''
                RiskLevel                                                = 10
                Resources                                                = @('https://www.stigviewer.com/stig/windows_7/2012-07-02/finding/V-1136')
            }
        }
        EnableSecuritySignature                           = @{Enable = $true
            Name                                      = 'Enable Security Signature'
            Parameters                                = @{Property = 'EnableSecuritySignature'
                ExpectedValue                           = 1
                OperationType                           = 'eq'
            }
            Details                                   = [ordered] @{Area = ''
                Description                                   = 'Microsoft network server: Digitally sign communications (if client agrees)'
                Resolution                                    = ''
                RiskLevel                                     = 10
                Resources                                     = @('https://support.microsoft.com/en-us/help/887429/overview-of-server-message-block-signing'
                    'https://community.spiceworks.com/topic/2131862-how-to-set-microsoft-network-server-digitally-sign-communications-always'
                    'https://www.stigviewer.com/stig/windows_server_2016/2017-11-20/finding/V-73663')
            }
        }
        RequireSecuritySignature                          = @{Enable = $true
            Name                                      = 'Require Security Signature'
            Parameters                                = @{Property = 'RequireSecuritySignature'
                ExpectedValue                            = 1
                OperationType                            = 'eq'
            }
            Details                                   = [ordered] @{Type = 'Security'
                Area                                           = ''
                Description                                    = 'Microsoft network server: Digitally sign communications (always)'
                Vulnerability                                  = 'Session hijacking uses tools that allow attackers who have access to the same network as the client
                computer or server to interrupt, end, or steal a session in progress. Attackers can potentially intercept and modify
                unsigned Server Message Block (SMB) packets and then modify the traffic and forward it so that the server might
                perform objectionable actions. Alternatively, the attacker could pose as the server or client after legitimate
                authentication and gain unauthorized access to data.
                SMB is the resource-sharing protocol that is supported by many Windows operating systems. It is the basis of NetBIOS
                and many other protocols. SMB signatures authenticate both users and the servers that host the data. If either side
                fails the authentication process, data transmission does not take place.'

                PotentialImpact                                = 'The Windows implementation of the SMB file and print-sharing protocol support mutual authentication,
                which prevents session hijacking attacks and supports message authentication to prevent man-in-the-middle attacks.
                SMB signing provides this authentication by placing a digital signature into each SMB, which is then verified by both the client computer and the server.
                Implementing SMB signing may negatively affect performance because each packet must be signed and verified. If these policy settings are enabled on a server that is performing multiple roles, such as a small business server that is serving as a domain controller, file server, print server, and application server, performance may be substantially slowed. Additionally, if you configure computers to ignore all unsigned SMB communications, older applications and operating systems cannot connect. However, if you completely disable all SMB signing, computers are vulnerable to session-hijacking attacks.'

                Resolution                                     = ''
                RiskLevel                                      = 10
                Resources                                      = @('https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/hh125918(v=ws.10)?redirectedfrom=MSDN#vulnerability'
                    'https://support.microsoft.com/en-us/help/887429/overview-of-server-message-block-signing'
                    'https://community.spiceworks.com/topic/2131862-how-to-set-microsoft-network-server-digitally-sign-communications-always')
            }
        }
    }
}
$LDAP = @{Enable = $true
    Source       = @{Name = 'LDAP Connectivity'
        Data              = { Test-LDAP -ComputerName $DomainController -WarningAction SilentlyContinue }
        Details           = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests        = [ordered] @{PortLDAP = @{Enable = $true
            Name                                   = 'LDAP Port is Available'
            Parameters                             = @{Property = 'LDAP'
                ExpectedValue                            = $true
                OperationType                            = 'eq'
            }
        }
        PortLDAPS                       = @{Enable = $true
            Name                            = 'LDAP SSL Port is Available'
            Parameters                      = @{Property = 'LDAPS'
                ExpectedValue             = $true
                OperationType             = 'eq'
            }
        }
        PortLDAP_GC                     = @{Enable = $true
            Name                            = 'LDAP GC Port is Available'
            Parameters                      = @{Property = 'GlobalCatalogLDAP'
                ExpectedValue               = $true
                OperationType               = 'eq'
            }
        }
        PortLDAPS_GC                    = @{Enable = $true
            Name                            = 'LDAP SSL GC Port is Available'
            Parameters                      = @{Property = 'GlobalCatalogLDAPS'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
    }
}
$LDAPInsecureBindings = @{Enable = $true
    Source                       = @{Name = 'LDAP Insecure Bindings'
        Data                              = { Get-WinADLDAPBindingsSummary -IncludeDomainControllers $DomainController -WarningAction SilentlyContinue }
        Details                           = [ordered] @{Area = 'Security'
            Description                   = 'LDAP channel binding and LDAP signing provide ways to increase the security of network communications between an Active Directory Domain Services (AD DS) or an Active Directory Lightweight Directory Services (AD LDS) and its clients. There is a vulerability in the default configuration for Lightweight Directory Access Protocol (LDAP) channel binding and LDAP signing and may expose Active directory domain controllers to elevation of privilege vulnerabilities. Microsoft Security Advisory ADV190023 address the issue by recommending the administrators enable LDAP channel binding and LDAP signing on Active Directory Domain Controllers. This hardening must be done manually until the release of the security update that will enable these settings by default.'
            Resolution                    = 'Make sure to remove any Clients performing simple or unsigned bindings.'
            RiskLevel                     = 10
            Resources                     = @('https://docs.microsoft.com/en-us/archive/blogs/russellt/identifying-clear-text-ldap-binds-to-your-dcs'
                'https://support.microsoft.com/en-us/help/4520412/2020-ldap-channel-binding-and-ldap-signing-requirement-for-windows')
        }
        ExpectedOutput                    = $false
    }
    Tests                        = [ordered] @{SimpleBinds = @{Enable = $true
            Name                                                      = 'Simple binds performed without SSL/TLS is 0'
            Parameters                                                = @{Property = 'Number of simple binds performed without SSL/TLS'
                ExpectedValue                               = 0
                OperationType                               = 'eq'
            }
        }
        UnsignedBinds                                      = @{Enable = $true
            Name                               = 'Negotiate/Kerberos/NTLM/Digest binds performed without signing is 0'
            Parameters                         = @{Property = 'Number of Negotiate/Kerberos/NTLM/Digest binds performed without signing'
                ExpectedValue                 = 0
                OperationType                 = 'eq'
            }
        }
    }
}
$MSSLegacy = @{Enable = $true
    Source            = @{Name = "MSS (Legacy)"
        Data                   = { Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -ComputerName $DomainController }
        Details                = [ordered] @{Type = 'Security'
            Area                          = ''
            Description                   = ''
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @('https://blogs.technet.microsoft.com/secguide/2016/10/02/the-mss-settings/')
        }
        Requirements           = @{CommandAvailable = 'Get-PSRegistry' }
        ExpectedOutput         = $true
    }
    Tests             = [ordered] @{DisableIPSourceRouting = @{Enable = $true
            Name                                                      = 'DisableIPSourceRouting'
            Parameters                                                = @{Property = 'DisableIPSourceRouting'
                ExpectedValue                                          = 2
                OperationType                                          = 'eq'
            }
            Details                                                   = [ordered] @{Area = ''
                Description                                                  = 'Highest protection, source routing is completely disabled'
                Resolution                                                   = ''
                RiskLevel                                                    = 10
                Resources                                                    = @('https://blogs.technet.microsoft.com/secguide/2016/10/02/the-mss-settings/')
            }
        }
        EnableICMPRedirect                                 = @{Enable = $true
            Name                                          = 'EnableICMPRedirect'
            Parameters                                    = @{Property = 'EnableICMPRedirect'
                ExpectedValue                      = 0
                OperationType                      = 'eq'
            }
            Details                                       = [ordered] @{Area = ''
                Description                              = ''
                Resolution                               = ''
                RiskLevel                                = 10
                Resources                                = @('https://blogs.technet.microsoft.com/secguide/2016/10/02/the-mss-settings/')
            }
        }
    }
}
$NetSessionEnumaration = @{Enable = $true
    Source                        = @{Name = "Net Session Enumaration"
        Data                               = { $Registry = Get-PSRegistry -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\DefaultSecurity"
            $CSD = [System.Security.AccessControl.CommonSecurityDescriptor]::new($true, $false, $Registry.SrvsvcSessionInfo, 0)
            $CSD.DiscretionaryAcl.SecurityIdentifier | Where-Object { $_ -eq 'S-1-5-11' } }
        Details                            = [ordered] @{Type = 'Security'
            Area                          = ''
            Description                   = 'Net Session Enumeration is a method used to retrieve information about established sessions on a server. Any domain user can query a server for its established sessions.'
            Resolution                    = 'Hardening Net Session Enumeration'
            RiskLevel                     = 10
            Resources                     = @('https://gallery.technet.microsoft.com/Net-Cease-Blocking-Net-1e8dcb5b')
        }
        Requirements                       = @{CommandAvailable = 'Get-PSRegistry' }
        ExpectedOutput                     = $false
    }
}
$NetworkCardSettings = @{Enable = $true
    Source                      = @{Name = "Get all network interfaces and firewall status"
        Data                             = { Get-ComputerNetwork -ComputerName $DomainController }
        Details                          = [ordered] @{Area = 'Connectivity'
            Category                   = ''
            Severity                   = ''
            RiskLevel                  = 0
            Description                = ''
            Resolution                 = ''
            Resources                  = @()
        }
    }
    Tests                       = [ordered] @{NETBIOSOverTCIP = @{Enable = $true
            Name                                                         = 'NetBIOS over TCIP should be disabled.'
            Parameters                                                   = @{Property = 'NetBIOSOverTCPIP'
                ExpectedValue                                   = 'Disabled'
                OperationType                                   = 'eq'
            }
            Details                                                      = @{Area = 'Connectivity'
                Category                                    = 'Legacy Protocols'
                Severity                                    = 'Critical'
                RiskLevel                                   = 90
                Description                                 = @'
                NetBIOS over TCP/IP is a networking protocol that allows legacy computer applications relying on the NetBIOS to be used on modern TCP/IP networks.
                Enabling NetBios might help an attackers access shared directories, files and also gain sensitive information such as computer name, domain, or workgroup.
'@

                Resolution                                  = 'Disable NetBIOS over TCPIP'
                Resources                                   = @('http://woshub.com/how-to-disable-netbios-over-tcpip-and-llmnr-using-gpo/')
            }
        }
        WindowsFirewall                                       = @{Enable = $true
            Name                                   = 'Windows Firewall should be enabled on network card'
            Parameters                             = @{Property = 'FirewallStatus'
                ExpectedValue                   = $true
                OperationType                   = 'eq'
                PropertyExtendedValue           = 'FirewallProfile'
            }
        }
    }
}
$NTDSParameters = @{Enable = $true
    Source                 = @{Name = "NTDS Parameters"
        Data                        = { Get-PSRegistry -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" -ComputerName $DomainController }
        Details                     = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                  = [ordered] @{DsaNotWritable = @{Enable = $true
            Name                                                   = 'Domain Controller should be writeable'
            Parameters                                             = @{Property = 'Dsa Not Writable'
                ExpectedOutput                                 = $false
            }
        }
    }
}
$OperatingSystem = @{Enable = $true
    Source                  = @{Name = 'Operating System'
        Data                         = { Get-ComputerOperatingSystem -ComputerName $DomainController -WarningAction SilentlyContinue }
        Details                      = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                   = [ordered] @{OperatingSystem = @{Enable = $true
            Name                                                     = 'Operating system Windows Server 2012 and up'
            Parameters                                               = @{Property = 'OperatingSystem'
                ExpectedValue                                   = @('Microsoft Windows Server 2019*', 'Microsoft Windows Server 2016*', 'Microsoft Windows Server 2012*')
                OperationType                                   = 'like'
                OperationResult                                 = 'OR'
                PropertyExtendedValue                           = 'OperatingSystem'
            }
        }
    }
}
$Pingable = @{Enable = $true
    Source           = @{Name = 'Ping Connectivity'
        Data                  = { Test-NetConnection -ComputerName $DomainController -WarningAction SilentlyContinue }
        Details               = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests            = [ordered] @{Ping = @{Enable = $true
            Name                                   = 'Responding to PING'
            Parameters                             = @{Property = 'PingSucceeded'
                PropertyExtendedValue                = 'PingReplyDetails', 'RoundtripTime'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
            }
        }
    }
}
$Ports = [ordered] @{Enable = $true
    Source                  = [ordered] @{Name = 'TCP Ports are open/closed as required'
        Data                                   = { $TcpPorts = @(53, 88, 135, 139, 389, 445, 464, 636, 3268, 3269, 9389)
            Test-ComputerPort -ComputerName $DomainController -PortTCP $TcpPorts -WarningAction SilentlyContinue }
        Requirements                           = @{CommandAvailable = 'Test-NetConnection' }
        Details                                = [ordered] @{Area = ''
            Category                             = ''
            Severity                             = ''
            RiskLevel                            = 0
            Description                          = ''
            Resolution                           = ''
            Resources                            = @()
        }
    }
    Tests                   = [ordered] @{Port53 = [ordered] @{Enable = $true
            Name                                                      = 'Port is OPEN'
            Parameters                                                = @{WhereObject = { $_.Port -eq '53' }
                Property                                            = 'Status'
                ExpectedValue                                       = $true
                OperationType                                       = 'eq'
                PropertyExtendedValue                               = 'Summary'
            }
        }
        Port88                                   = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '88' }
                Property                            = 'Status'
                ExpectedValue                       = $true
                OperationType                       = 'eq'
                PropertyExtendedValue               = 'Summary'
            }
        }
        Port135                                  = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '135' }
                Property                             = 'Status'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
                PropertyExtendedValue                = 'Summary'
            }
        }
        Port139                                  = [ordered] @{Enable = $true
            Name                                    = 'Port is CLOSED'
            Parameters                              = @{WhereObject = { $_.Port -eq '139' }
                Property                             = 'Status'
                ExpectedValue                        = $false
                OperationType                        = 'eq'
                PropertyExtendedValue                = 'Summary'
            }
            Details                                 = [ordered] @{Area = ''
                Category                                = ''
                Severity                                = ''
                RiskLevel                               = 0
                Description                             = @'
                NetBIOS over TCP/IP is a networking protocol that allows legacy computer applications relying on the NetBIOS to be used on modern TCP/IP networks.
                Enabling NetBios might help an attackers access shared directories, files and also gain sensitive information such as computer name, domain, or workgroup.
'@

                Resolution                              = 'Disable NETBIOS over TCPIP'
                Resources                               = @('http://woshub.com/how-to-disable-netbios-over-tcpip-and-llmnr-using-gpo/')
            }
        }
        Port445                                  = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '445' }
                Property                             = 'Status'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
                PropertyExtendedValue                = 'Summary'
            }
        }
        Port464                                  = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '464' }
                Property                             = 'Status'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
                PropertyExtendedValue                = 'Summary'
            }
        }
        Port636                                  = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '636' }
                Property                             = 'Status'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
                PropertyExtendedValue                = 'Summary'
            }
        }
        Port3268                                 = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '3268' }
                Property                              = 'Status'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
                PropertyExtendedValue                 = 'Summary'
            }
        }
        Port3269                                 = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '3269' }
                Property                              = 'Status'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
                PropertyExtendedValue                 = 'Summary'
            }
        }
        Port9389                                 = [ordered] @{Enable = $true
            Name                                    = 'Port is OPEN'
            Parameters                              = @{WhereObject = { $_.Port -eq '9389' }
                Property                              = 'Status'
                ExpectedValue                         = $true
                OperationType                         = 'eq'
                PropertyExtendedValue                 = 'Summary'
            }
        }
    }
}
$RDPPorts = [ordered] @{Enable = $false
    Source                     = [ordered] @{Name = 'RDP Port is open'
        Data                                      = { Test-ComputerPort -ComputerName $DomainController -PortTCP 3389 -WarningAction SilentlyContinue }
        Details                                   = [ordered] @{Area = ''
            Description                          = ''
            Resolution                           = ''
            RiskLevel                            = 10
            Resources                            = @()
        }
    }
    Tests                      = [ordered] @{PortOpen = [ordered] @{Enable = $false
            Name                                                           = 'Port is OPEN'
            Parameters                                                     = @{Property = 'Status'
                ExpectedValue                                      = $true
                OperationType                                      = 'eq'
                PropertyExtendedValue                              = 'Summary'
            }
        }
    }
}
$RDPSecurity = [ordered] @{Enable = $true
    Source                        = [ordered] @{Name = 'RDP Security'
        Data                                         = { Get-ComputerRDP -ComputerName $DomainController -WarningAction SilentlyContinue }
        Details                                      = [ordered] @{Area = 'Connectivity'
            Description                          = ''
            Resolution                           = ''
            RiskLevel                            = 10
            Resources                            = @()
        }
    }
    Tests                         = [ordered] @{PortOpen = [ordered] @{Enable = $true
            Name                                                              = 'Port is OPEN'
            Parameters                                                        = @{Property   = 'Connectivity'
                ExpectedValue         = $true
                OperationType         = 'eq'
                PropertyExtendedValue = 'ConnectivitySummary'
            }
            Details                                                           = [ordered] @{Area = 'Connectivity'
                Description                                              = ''
                Resolution                                               = ''
                RiskLevel                                                = 10
                Resources                                                = @('https://lazywinadmin.com/2014/04/powershell-getset-network-level.html'
                    'https://devblogs.microsoft.com/scripting/weekend-scripter-report-on-network-level-authentication/')
            }
        }
        NLAAuthenticationEnabled                         = [ordered] @{Enable = $true
            Name                                      = 'NLA Authentication is Enabled'
            Parameters                                = @{Property = 'UserAuthenticationRequired'
                ExpectedValue                                      = $true
                OperationType                                      = 'eq'
            }
            Details                                   = [ordered] @{Area = 'Connectivity'
                Description                                              = ''
                Resolution                                               = ''
                RiskLevel                                                = 10
                Resources                                                = @('https://lazywinadmin.com/2014/04/powershell-getset-network-level.html'
                    'https://devblogs.microsoft.com/scripting/weekend-scripter-report-on-network-level-authentication/')
            }
        }
        MinimalEncryptionLevel                           = [ordered] @{Enable = $true
            Name                                      = 'Minimal Encryption Level is set to at least High'
            Parameters                                = @{Property = 'MinimalEncryptionLevelValue'
                ExpectedValue                                    = 3
                OperationType                                    = 'ge'
                PropertyExtendedValue                            = 'MinimalEncryptionLevel'
            }
            Details                                   = [ordered] @{Area = 'Connectivity'
                Description                                            = 'Remote connections must be encrypted to prevent interception of data or sensitive information. Selecting "High Level" will ensure encryption of Remote Desktop Services sessions in both directions.'
                Resolution                                             = ''
                RiskLevel                                              = 10
                Resources                                              = @('https://www.stigviewer.com/stig/windows_server_2012_member_server/2014-01-07/finding/V-3454')
            }
        }
    }
}
$Services = [ordered] @{Enable = $true
    Source                     = @{Name = 'Service Status'
        Data                            = { $Services = @('ADWS', 'DNS', 'DFS', 'DFSR', 'Eventlog', 'EventSystem', 'KDC', 'LanManWorkstation', 'LanManServer', 'NetLogon', 'NTDS', 'RPCSS', 'SAMSS', 'Spooler', 'W32Time')
            Get-PSService -Computers $DomainController -Services $Services }
        Details                         = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                      = [ordered] @{ADWSServiceStatus = @{Enable = $true
            Name                                                          = 'ADWS Service is RUNNING'
            Parameters                                                    = @{WhereObject = { $_.Name -eq 'ADWS' }
                Property                                             = 'Status'
                ExpectedValue                                        = 'Running'
                OperationType                                        = 'eq'
            }
        }
        ADWSServiceStartType                                   = @{Enable = $true
            Name                                     = 'ADWS Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'ADWS' }
                Property                                = 'StartType'
                ExpectedValue                           = 'Automatic'
                OperationType                           = 'eq'
            }
        }
        DNSServiceStatus                                       = @{Enable = $true
            Name                                     = 'DNS Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'DNS' }
                Property                            = 'Status'
                ExpectedValue                       = 'Running'
                OperationType                       = 'eq'
            }
        }
        DNSServiceStartType                                    = @{Enable = $true
            Name                                     = 'DNS Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'DNS' }
                Property                               = 'StartType'
                ExpectedValue                          = 'Automatic'
                OperationType                          = 'eq'
            }
        }
        DFSServiceStatus                                       = @{Enable = $true
            Name                                     = 'DFS Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'DFS' }
                Property                            = 'Status'
                ExpectedValue                       = 'Running'
                OperationType                       = 'eq'
            }
        }
        DFSServiceStartType                                    = @{Enable = $true
            Name                                     = 'DFS Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'DFS' }
                Property                               = 'StartType'
                ExpectedValue                          = 'Automatic'
                OperationType                          = 'eq'
            }
        }
        DFSRServiceStatus                                      = @{Enable = $true
            Name                                     = 'DFSR Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'DFSR' }
                Property                             = 'Status'
                ExpectedValue                        = 'Running'
                OperationType                        = 'eq'
            }
        }
        DFSRServiceStartType                                   = @{Enable = $true
            Name                                     = 'DFSR Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'DFSR' }
                Property                                = 'StartType'
                ExpectedValue                           = 'Automatic'
                OperationType                           = 'eq'
            }
        }
        EventlogServiceStatus                                  = @{Enable = $true
            Name                                     = 'Eventlog Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'Eventlog' }
                Property                                 = 'Status'
                ExpectedValue                            = 'Running'
                OperationType                            = 'eq'
            }
        }
        EventlogServiceStartType                               = @{Enable = $true
            Name                                     = 'Eventlog Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'Eventlog' }
                Property                                    = 'StartType'
                ExpectedValue                               = 'Automatic'
                OperationType                               = 'eq'
            }
        }
        EventSystemServiceStatus                               = @{Enable = $true
            Name                                     = 'EventSystem Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'EventSystem' }
                Property                                    = 'Status'
                ExpectedValue                               = 'Running'
                OperationType                               = 'eq'
            }
        }
        EventSystemServiceStartType                            = @{Enable = $true
            Name                                     = 'EventSystem Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'EventSystem' }
                Property                                       = 'StartType'
                ExpectedValue                                  = 'Automatic'
                OperationType                                  = 'eq'
            }
        }
        KDCServiceStatus                                       = @{Enable = $true
            Name                                     = 'KDC Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'KDC' }
                Property                            = 'Status'
                ExpectedValue                       = 'Running'
                OperationType                       = 'eq'
            }
        }
        KDCServiceStartType                                    = @{Enable = $true
            Name                                     = 'KDC Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'KDC' }
                Property                               = 'StartType'
                ExpectedValue                          = 'Automatic'
                OperationType                          = 'eq'
            }
        }
        LanManWorkstationServiceStatus                         = @{Enable = $true
            Name                                     = 'LanManWorkstation Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'LanManWorkstation' }
                Property                                          = 'Status'
                ExpectedValue                                     = 'Running'
                OperationType                                     = 'eq'
            }
        }
        LanManWorkstationServiceStartType                      = @{Enable = $true
            Name                                     = 'LanManWorkstation Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'LanManWorkstation' }
                Property                                             = 'StartType'
                ExpectedValue                                        = 'Automatic'
                OperationType                                        = 'eq'
            }
        }
        LanManServerServiceStatus                              = @{Enable = $true
            Name                                     = 'LanManServer Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'LanManServer' }
                Property                                     = 'Status'
                ExpectedValue                                = 'Running'
                OperationType                                = 'eq'
            }
        }
        LanManServerServiceStartType                           = @{Enable = $true
            Name                                     = 'LanManServer Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'LanManServer' }
                Property                                        = 'StartType'
                ExpectedValue                                   = 'Automatic'
                OperationType                                   = 'eq'
            }
        }
        NetLogonServiceStatus                                  = @{Enable = $true
            Name                                     = 'NetLogon Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'NetLogon' }
                Property                                 = 'Status'
                ExpectedValue                            = 'Running'
                OperationType                            = 'eq'
            }
        }
        NetLogonServiceStartType                               = @{Enable = $true
            Name                                     = 'NetLogon Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'NetLogon' }
                Property                                    = 'StartType'
                ExpectedValue                               = 'Automatic'
                OperationType                               = 'eq'
            }
        }
        NTDSServiceStatus                                      = @{Enable = $true
            Name                                     = 'NTDS Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'NTDS' }
                Property                             = 'Status'
                ExpectedValue                        = 'Running'
                OperationType                        = 'eq'
            }
        }
        NTDSServiceStartType                                   = @{Enable = $true
            Name                                     = 'NTDS Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'NTDS' }
                Property                                = 'StartType'
                ExpectedValue                           = 'Automatic'
                OperationType                           = 'eq'
            }
        }
        RPCSSServiceStatus                                     = @{Enable = $true
            Name                                     = 'RPCSS Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'RPCSS' }
                Property                              = 'Status'
                ExpectedValue                         = 'Running'
                OperationType                         = 'eq'
            }
        }
        RPCSSServiceStartType                                  = @{Enable = $true
            Name                                     = 'RPCSS Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'RPCSS' }
                Property                                 = 'StartType'
                ExpectedValue                            = 'Automatic'
                OperationType                            = 'eq'
            }
        }
        SAMSSServiceStatus                                     = @{Enable = $true
            Name                                     = 'SAMSS Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'SAMSS' }
                Property                              = 'Status'
                ExpectedValue                         = 'Running'
                OperationType                         = 'eq'
            }
        }
        SAMSSServiceStartType                                  = @{Enable = $true
            Name                                     = 'SAMSS Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'SAMSS' }
                Property                                 = 'StartType'
                ExpectedValue                            = 'Automatic'
                OperationType                            = 'eq'
            }
        }
        SpoolerServiceStatus                                   = @{Enable = $true
            Name                                     = 'Spooler Service is STOPPED'
            Parameters                               = @{WhereObject = { $_.Name -eq 'Spooler' }
                Property                                = 'Status'
                ExpectedValue                           = 'Stopped'
                OperationType                           = 'eq'
            }
            Details                                  = [ordered] @{Area = 'Security'
                Category                                   = 'Services'
                Severity                                   = ''
                RiskLevel                                  = 0
                Description                                = 'Due to security concerns SPOOLER should be disabled and stopped. However in some cases it may be required to have SPOOLER service up and running to cleanup stale printer objects from AD.'
                Resolution                                 = ''
                Resources                                  = @('https://adsecurity.org/?p=4056'
                    'https://docs.microsoft.com/en-us/windows-server/security/windows-services/security-guidelines-for-disabling-system-services-in-windows-server#print-spooler')
            }
        }
        SpoolerServiceStartType                                = @{Enable = $true
            Name                                     = 'Spooler Service START TYPE is DISABLED'
            Parameters                               = @{WhereObject = { $_.Name -eq 'Spooler' }
                Property                                   = 'StartType'
                ExpectedValue                              = 'Disabled'
                OperationType                              = 'eq'
            }
            Details                                  = [ordered] @{Area = 'Security'
                Category                                      = 'Services'
                Severity                                      = ''
                RiskLevel                                     = 0
                Description                                   = 'Due to security concerns SPOOLER should be disabled and stopped. However in some cases it may be required to have SPOOLER service up and running to cleanup stale printer objects from AD.'
                Resolution                                    = ''
                Resources                                     = @('https://adsecurity.org/?p=4056'
                    'https://docs.microsoft.com/en-us/windows-server/security/windows-services/security-guidelines-for-disabling-system-services-in-windows-server#print-spooler')
            }
        }
        W32TimeServiceStatus                                   = @{Enable = $true
            Name                                     = 'W32Time Service is RUNNING'
            Parameters                               = @{WhereObject = { $_.Name -eq 'W32Time' }
                Property                                = 'Status'
                ExpectedValue                           = 'Running'
                OperationType                           = 'eq'
            }
        }
        W32TimeServiceStartType                                = @{Enable = $true
            Name                                     = 'W32Time Service START TYPE is Automatic'
            Parameters                               = @{WhereObject = { $_.Name -eq 'W32Time' }
                Property                                   = 'StartType'
                ExpectedValue                              = 'Automatic'
                OperationType                              = 'eq'
            }
        }
    }
}
$ServiceWINRM = @{Enable = $true
    Source               = @{Name = "Service WINRM"
        Data                      = { Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service' -ComputerName $DomainController }
        Details                   = [ordered] @{Type = 'Security'
            Area                          = ''
            Description                   = 'Storage of administrative credentials could allow unauthorized access. Disallowing the storage of RunAs credentials for Windows Remote Management will prevent them from being used with plug-ins. The Windows Remote Management (WinRM) service must not store RunAs credentials.'
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @()
        }
        Requirements              = @{CommandAvailable = 'Get-PSRegistry' }
        ExpectedOutput            = $true
    }
    Tests                = [ordered] @{DisableRunAs = @{Enable = $true
            Name                                               = 'DisableRunAs'
            Parameters                                         = @{Property = 'DisableRunAs'
                ExpectedValue                                = 1
                OperationType                                = 'eq'
            }
            Details                                            = [ordered] @{Area = ''
                Description                                        = 'Storage of administrative credentials could allow unauthorized access. Disallowing the storage of RunAs credentials for Windows Remote Management will prevent them from being used with plug-ins. The Windows Remote Management (WinRM) service must not store RunAs credentials.'
                Resolution                                         = ''
                RiskLevel                                          = 10
                Resources                                          = @('https://www.stigviewer.com/stig/windows_server_2016/2018-03-07/finding/V-73603')
            }
        }
    }
}
$SMBProtocols = @{Enable = $true
    Source               = @{Name = 'SMB Protocols'
        Data                      = { Get-ComputerSMB -ComputerName $DomainController }
        Details                   = [ordered] @{Area = ''
            Description                 = ''
            Resolution                  = ''
            RiskLevel                   = 10
            Resources                   = @('https://community.spiceworks.com/topic/2153374-bpa-on-windows-server-2016-warns-about-smb-not-in-a-default-configuration')
        }
        Requirements              = @{CommandAvailable = 'Get-ComputerSMB' }
    }
    Tests                = [ordered] @{AsynchronousCredits = @{Enable = $true
            Name                                                      = 'AsynchronousCredits'
            Parameters                                                = @{Property = 'AsynchronousCredits'
                ExpectedValue                                       = 64
                OperationType                                       = 'eq'
            }
            Details                                                   = [ordered] @{Area = ''
                Description                                               = 'AsynchronousCredits should have the recommended value'
                Resolution                                                = ''
                RiskLevel                                                 = 10
                Resources                                                 = @()
            }
        }
        AutoDisconnectTimeout                              = @{Enable = $true
            Name                                       = 'AutoDisconnectTimeout'
            Parameters                                 = @{Property = 'AutoDisconnectTimeout'
                ExpectedValue                         = 0
                OperationType                         = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                                 = 'AutoDisconnectTimeout should have the recommended value'
                Resolution                                  = ''
                RiskLevel                                   = 10
                Resources                                   = @()
            }
        }
        CachedOpenLimit                                    = @{Enable = $true
            Name                                       = 'CachedOpenLimit'
            Parameters                                 = @{Property = 'CachedOpenLimit'
                ExpectedValue                   = 5
                OperationType                   = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                           = 'CachedOpenLimit should have the recommended value'
                Resolution                            = ''
                RiskLevel                             = 10
                Resources                             = @()
            }
        }
        DurableHandleV2TimeoutInSeconds                    = @{Enable = $true
            Name                                       = 'DurableHandleV2TimeoutInSeconds'
            Parameters                                 = @{Property = 'DurableHandleV2TimeoutInSeconds'
                ExpectedValue                                   = 30
                OperationType                                   = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                                           = 'DurableHandleV2TimeoutInSeconds should have the recommended value'
                Resolution                                            = ''
                RiskLevel                                             = 10
                Resources                                             = @()
            }
        }
        EnableSMB1Protocol                                 = @{Enable = $true
            Name                                       = 'SMB v1 Protocol should be disabled'
            Parameters                                 = @{Property = 'EnableSMB1Protocol'
                ExpectedValue                      = $false
                OperationType                      = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                              = ''
                Resolution                               = ''
                RiskLevel                                = 10
                Resources                                = @()
            }
        }
        EnableSMB2Protocol                                 = @{Enable = $true
            Name                                       = 'SMB v2 Protocol should be enabled'
            Parameters                                 = @{Property = 'EnableSMB2Protocol'
                ExpectedValue                      = $true
                OperationType                      = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                              = ''
                Resolution                               = ''
                RiskLevel                                = 10
                Resources                                = @()
            }
        }
        MaxThreadsPerQueue                                 = @{Enable = $true
            Name                                       = 'MaxThreadsPerQueue'
            Parameters                                 = @{Property = 'MaxThreadsPerQueue'
                ExpectedValue                      = 20
                OperationType                      = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                              = 'MaxThreadsPerQueue should have the recommended value'
                Resolution                               = ''
                RiskLevel                                = 10
                Resources                                = @()
            }
        }
        Smb2CreditsMin                                     = @{Enable = $true
            Name                                       = 'Smb2CreditsMin'
            Parameters                                 = @{Property = 'Smb2CreditsMin'
                ExpectedValue                  = 128
                OperationType                  = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                          = 'Smb2CreditsMin should have the recommended value'
                Resolution                           = ''
                RiskLevel                            = 10
                Resources                            = @()
            }
        }
        Smb2CreditsMax                                     = @{Enable = $true
            Name                                       = 'Smb2CreditsMax'
            Parameters                                 = @{Property = 'Smb2CreditsMax'
                ExpectedValue                  = 2048
                OperationType                  = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                          = 'Smb2CreditsMax should have the recommended value'
                Resolution                           = ''
                RiskLevel                            = 10
                Resources                            = @('https://github.com/EvotecIT/Testimo/issues/50')
            }
        }
        RequireSecuritySignature                           = @{Enable = $true
            Name                                       = 'SMB v2 Require Security Signature'
            Parameters                                 = @{Property = 'RequireSecuritySignature'
                ExpectedValue                            = $true
                OperationType                            = 'eq'
            }
            Details                                    = [ordered] @{Area = ''
                Description                                    = ''
                Resolution                                     = ''
                RiskLevel                                      = 10
                Resources                                      = @()
            }
        }
    }
}
$SMBShares = @{Enable = $true
    Source            = @{Name = 'Default SMB Shares'
        Data                   = { Get-ComputerSMBShare -ComputerName $DomainController }
        Details                = [ordered] @{Area = ''
            Description                 = ''
            Resolution                  = ''
            RiskLevel                   = 10
            Resources                   = @()
        }
        Requirements           = @{CommandAvailable = 'Get-ComputerSMBShare' }
    }
    Tests             = [ordered] @{AdminShare = @{Enable = $true
            Name                                          = 'Remote Admin Share is available'
            Parameters                                    = @{WhereObject = { $_.Name -eq 'ADMIN$' }
                ExpectedCount                                 = 1
                PropertyExtendedValue                         = 'Path'
            }
        }
        DefaultShare                           = @{Enable = $true
            Name                              = 'Default Share is available'
            Parameters                        = @{WhereObject = { $_.Name -eq 'C$' }
                ExpectedCount                   = 1
                PropertyExtendedValue           = 'Path'
            }
        }
        RemoteIPC                              = @{Enable = $true
            Name                              = 'Remote IPC Share is available'
            Parameters                        = @{WhereObject = { $_.Name -eq 'IPC$' }
                ExpectedCount                = 1
                PropertyExtendedValue        = 'Path'
            }
        }
        NETLOGON                               = @{Enable = $true
            Name                              = 'NETLOGON Share is available'
            Parameters                        = @{WhereObject = { $_.Name -eq 'NETLOGON' }
                ExpectedCount               = 1
                PropertyExtendedValue       = 'Path'
            }
        }
        SYSVOL                                 = @{Enable = $true
            Name                              = 'SYSVOL Share is available'
            Parameters                        = @{WhereObject = { $_.Name -eq 'SYSVOL' }
                ExpectedCount             = 1
                PropertyExtendedValue     = 'Path'
            }
        }
    }
}
$TimeSettings = [ordered] @{Enable = $true
    Source                         = @{Name = "Time Settings"
        Data                                = { Get-TimeSetttings -ComputerName $DomainController -Domain $Domain }
        Details                             = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                          = [ordered] @{NTPServerEnabled = @{Enable = $true
            Name                                                             = 'NtpServer must be enabled.'
            Parameters                                                       = @{WhereObject = { $_.ComputerName -eq $DomainController }
                Property                                            = 'NtpServerEnabled'
                ExpectedValue                                       = $true
                OperationType                                       = 'eq'
            }
        }
        NTPServerIntervalMissing                                  = @{Enable = $true
            Name                                    = 'Ntp Server Interval should be set'
            Parameters                              = @{WhereObject = { $_.ComputerName -eq $DomainController }
                Property                                    = 'NtpServerIntervals'
                ExpectedValue                               = 'Missing'
                OperationType                               = 'notcontains'
            }
        }
        NTPServerIntervalIncorrect                                = @{Enable = $true
            Name                                    = 'Ntp Server Interval should be within known settings'
            Parameters                              = @{WhereObject = { $_.ComputerName -eq $DomainController }
                Property                                      = 'NtpServerIntervals'
                ExpectedValue                                 = 'Incorrect'
                OperationType                                 = 'notcontains'
            }
        }
        VMTimeProvider                                            = @{Enable = $true
            Name                                    = 'Virtual Machine Time Provider should be disabled.'
            Parameters                              = @{WhereObject = { $_.ComputerName -eq $DomainController }
                Property                          = 'VMTimeProvider'
                ExpectedValue                     = $false
                OperationType                     = 'eq'
            }
        }
        NtpTypeNonPDC                                             = [ordered] @{Enable = $true
            Name                                              = 'NTP Server should be set to Domain Hierarchy'
            Requirements                                      = @{IsPDC = $false }
            Parameters                                        = @{WhereObject = { $_.ComputerName -eq $DomainController }
                Property                                   = 'NtpType'
                ExpectedValue                              = 'NT5DS'
                OperationType                              = 'eq'
            }
        }
        NtpTypePDC                                                = [ordered] @{Enable = $true
            Name                                              = 'NTP Server should be set to AllSync'
            Requirements                                      = @{IsPDC = $true }
            Parameters                                        = @{WhereObject = { $_.ComputerName -eq $DomainController }
                Property                                = 'NtpType'
                ExpectedValue                           = 'AllSync'
                OperationType                           = 'eq'
            }
        }
    }
}
$TimeSynchronizationExternal = @{Enable = $true
    Source                              = @{Name = "Time Synchronization External"
        Data                                     = { Get-ComputerTime -TimeTarget $DomainController -WarningAction SilentlyContinue @SourceParameters }
        Parameters                               = @{TimeSource = 'pool.ntp.org' }
        Details                                  = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                               = [ordered] @{TimeSynchronizationTest = @{Enable = $true
            Name                                                                         = 'Time Difference'
            Parameters                                                                   = @{Property   = 'TimeDifferenceSeconds'
                ExpectedValue         = 1
                OperationType         = 'le'
                PropertyExtendedValue = 'TimeDifferenceSeconds'
            }
        }
    }
    MicrosoftMaterials                  = 'https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc773263(v=ws.10)#w2k3tr_times_tools_uhlp'
}
$TimeSynchronizationInternal = @{Enable = $true
    Source                              = @{Name = "Time Synchronization Internal"
        Data                                     = { Get-ComputerTime -TimeTarget $DomainController -WarningAction SilentlyContinue }
        Details                                  = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @('https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc773263(v=ws.10)#w2k3tr_times_tools_uhlp')
        }
    }
    Tests                               = [ordered] @{LastBootUpTime = @{Enable = $true
            Name                                                                = 'Last Boot Up time should be less than X days'
            Parameters                                                          = @{Property = 'LastBootUpTime'
                ExpectedValue                                  = '(Get-Date).AddDays(-60)'
                OperationType                                  = 'gt'
            }
        }
        TimeSynchronizationTest                                      = @{Enable = $true
            Name                                  = 'Time Difference'
            Parameters                            = @{Property = 'TimeDifferenceSeconds'
                ExpectedValue                           = 1
                OperationType                           = 'le'
                PropertyExtendedValue                   = 'TimeDifferenceSeconds'
            }
        }
    }
}
$WindowsRemoteManagement = @{Enable = $true
    Source                          = @{Name = 'Windows Remote Management'
        Data                                 = { Test-WinRM -ComputerName $DomainController }
        Details                              = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                           = [ordered] @{WindowsRemoteManagement = @{Enable1 = $true
            Name                                                                      = 'Test submits an identification request that determines whether the WinRM service is running.'
            Parameters                                                                = @{Property = 'Status'
                ExpectedValue                                            = $true
                OperationType                                            = 'eq'
            }
        }
    }
}
$WindowsRolesAndFeatures = @{Enable = $true
    Source                          = @{Name = "Windows Roles and Features"
        Data                                 = { Get-WindowsFeature -ComputerName $DomainController }
    }
    Tests                           = [ordered] @{ActiveDirectoryDomainServices = @{Enable = $true
            Name                                                                           = 'Active Directory Domain Services is installed'
            Parameters                                                                     = @{WhereObject = { $_.Name -eq 'AD-Domain-Services' }
                Property                                                         = 'Installed'
                ExpectedValue                                                    = $true
                OperationType                                                    = 'eq'
            }
        }
        DNSServer                                                               = @{Enable = $true
            Name                                                 = 'DNS Server is installed'
            Parameters                                           = @{WhereObject = { $_.Name -eq 'DNS' }
                Property                     = 'Installed'
                ExpectedValue                = $true
                OperationType                = 'eq'
            }
        }
        FileandStorageServices                                                  = @{Enable = $true
            Name                                                 = 'File and Storage Services is installed'
            Parameters                                           = @{WhereObject = { $_.Name -eq 'FileAndStorage-Services' }
                Property                                  = 'Installed'
                ExpectedValue                             = $true
                OperationType                             = 'eq'
            }
        }
        FileandiSCSIServices                                                    = @{Enable = $true
            Name                                                 = 'File and iSCSI Services is installed'
            Parameters                                           = @{WhereObject = { $_.Name -eq 'File-Services' }
                Property                                = 'Installed'
                ExpectedValue                           = $true
                OperationType                           = 'eq'
            }
        }
        FileServer                                                              = @{Enable = $true
            Name                                                 = 'File Server is installed'
            Parameters                                           = @{WhereObject = { $_.Name -eq 'FS-FileServer' }
                Property                      = 'Installed'
                ExpectedValue                 = $true
                OperationType                 = 'eq'
            }
        }
        StorageServices                                                         = @{Enable = $true
            Name                                                 = 'Storage Services is installed'
            Parameters                                           = @{WhereObject = { $_.Name -eq 'Storage-Services' }
                Property                           = 'Installed'
                ExpectedValue                      = $true
                OperationType                      = 'eq'
            }
        }
        WindowsPowerShell51                                                     = @{Enable = $true
            Name                                                 = 'Windows PowerShell 5.1 is installed'
            Parameters                                           = @{WhereObject = { $_.Name -eq 'PowerShell' }
                Property                               = 'Installed'
                ExpectedValue                          = $true
                OperationType                          = 'eq'
            }
        }
    }
}
$WindowsUpdates = @{Enable = $true
    Source                 = @{Name = "Windows Updates"
        Data                        = { Get-HotFix -ComputerName $DomainController | Sort-Object -Property InstalledOn -Descending | Select-Object -First 1 }
        Details                     = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                  = [ordered] @{WindowsUpdates = @{Enable = $true
            Name                                                   = 'Last Windows Updates should be less than X days ago'
            Parameters                                             = @{Property = 'InstalledOn'
                ExpectedValue                                  = '(Get-Date).AddDays(-60)'
                OperationType                                  = 'gt'
            }
        }
    }
}
$Backup = @{Enable = $true
    Source         = @{Name = 'Forest Backup'
        Data                = { Get-WinADLastBackup }
        Details             = [ordered] @{Area = 'Backup'
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests          = [ordered] @{LastBackupTests = @{Enable = $true
            Name                                            = 'Forest Last Backup Time - Context'
            Parameters                                      = @{ExpectedValue = 2
                OperationType                                        = 'lt'
                Property                                             = 'LastBackupDaysAgo'
                PropertyExtendedValue                                = 'LastBackup'
                OverwriteName                                        = { "Last Backup $($_.NamingContext)" }
            }
        }
    }
}
$ForestFSMORoles = @{Enable = $true
    Source                  = @{Name = 'Roles availability'
        Data                         = { Test-ADRolesAvailability }
        Details                      = [ordered] @{Area = 'Features'
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                   = [ordered] @{SchemaMasterAvailability = @{Enable = $true
            Name                                                              = 'Schema Master Availability'
            Parameters                                                        = @{ExpectedValue = $true
                Property                                                      = 'SchemaMasterAvailability'
                OperationType                                                 = 'eq'
                PropertyExtendedValue                                         = 'SchemaMaster'
            }
        }
        DomainNamingMasterAvailability                             = @{Enable = $true
            Name                                            = 'Domain Master Availability'
            Parameters                                      = @{ExpectedValue = $true
                Property                                            = 'DomainNamingMasterAvailability'
                OperationType                                       = 'eq'
                PropertyExtendedValue                               = 'DomainNamingMaster'
            }
        }
    }
}
$ObjectsWithConflict = @{Enable = $true
    Source                      = @{Name = 'Objects with Conflict (Duplicate RDN)'
        Data                             = { Get-WinADForestObjectsConflict }
        ExpectedOutput                   = $false
        Details                          = [ordered] @{Area = 'Features'
            Description                   = "When two objects are created with the same Relative Distinguished Name (RDN) in the same parent Organizational Unit or container, the conflict is recognized by the system when one of the new objects replicates to another domain controller. When this happens, one of the objects is renamed. Some sources say the RDN is mangled to make it unique. The new RDN will be <Old RDN>\0ACNF:<objectGUID>"
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @('https://social.technet.microsoft.com/wiki/contents/articles/15435.active-directory-duplicate-object-name-resolution.aspx'
                'http://ourwinblog.blogspot.com/2011/05/resolving-computer-object-replication.html'
                'https://kickthatcomputer.wordpress.com/2014/11/22/seek-and-destroy-duplicate-ad-objects-with-cnf-in-the-name/'
                'https://gallery.technet.microsoft.com/scriptcenter/Get-ADForestConflictObjects-4667fa37')
        }
    }
}
$OptionalFeatures = [ordered] @{Enable = $true
    Source                             = [ordered] @{Name = 'Optional Features'
        Data                                              = { $ADModule = Import-PrivateModule PSWinDocumentation.AD
            & $ADModule { Get-WinADForestOptionalFeatures -WarningAction SilentlyContinue } }
        Details                                           = [ordered] @{Area = 'Features'
            Description                          = ''
            Resolution                           = ''
            RiskLevel                            = 10
            Resources                            = @()
        }
    }
    Tests                              = [ordered] @{RecycleBinEnabled = @{Enable = $true
            Name                                                                  = 'Recycle Bin Enabled'
            Parameters                                                            = @{Property = 'Recycle Bin Enabled'
                ExpectedValue                                     = $true
                OperationType                                     = 'eq'
            }
        }
        LapsAvailable                                                  = @{Enable = $true
            Name                                     = 'LAPS Schema Extended'
            Parameters                               = @{Property = 'Laps Enabled'
                ExpectedValue                 = $true
                OperationType                 = 'eq'
            }
        }
        PrivAccessManagement                                           = @{Enable = $true
            Name                                     = 'Privileged Access Management Enabled'
            Parameters                               = @{Property = 'Privileged Access Management Feature Enabled'
                ExpectedValue                        = $true
                OperationType                        = 'eq'
            }
        }
    }
}
$OrphanedAdmins = @{Enable = $true
    Source                 = @{Name = 'Orphaned Administrative Objects (AdminCount)'
        Data                        = { Get-WinADPriviligedObjects -OrphanedOnly }
        ExpectedOutput              = $false
        Details                     = [ordered] @{Area = 'Features'
            Description                   = "Consider this: a user is stamped with an AdminCount of 1, as a result of being added to Domain Admins; the user is removed from Domain Admins; the AdminCount value persists. In this instance the user is considered as orphaned. The ramifications? The AdminSDHolder ACL will be stamped upon this user every hour to protect against tampering. In turn, this can cause unexpected issues with delegation and application permissions."
            Resolution                    = ''
            RiskLevel                     = 10
            Resources                     = @('https://blogs.technet.microsoft.com/poshchap/2016/07/29/security-focus-orphaned-admincount-eq-1-ad-users/')
        }
    }
}
$Replication = @{Enable = $true
    Source              = @{Name = 'Forest Replication'
        Data                     = { Get-WinADForestReplication -WarningAction SilentlyContinue }
        Details                  = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests               = [ordered] @{ReplicationTests = @{Enable = $true
            Name                                                  = 'Replication Test'
            Parameters                                            = @{ExpectedValue = $true
                Property                                              = 'Status'
                OperationType                                         = 'eq'
                PropertyExtendedValue                                 = 'StatusMessage'
                OverwriteName                                         = { "Replication from $($_.Server) to $($_.ServerPartner)" }
            }
        }
    }
}
$ReplicationStatus = @{Enable = $true
    Source                    = @{Name = 'Forest Replication using RepAdmin'
        Data                           = { $Header = '"showrepl_COLUMNS","Destination DSA Site","Destination DSA","Naming Context","Source DSA Site","Source DSA","Transport Type","Number of Failures","Last Failure Time","Last Success Time","Last Failure Status"'
            $data = repadmin /showrepl * /csv
            $data[0] = $Header
            $data | ConvertFrom-Csv }
        Details                        = [ordered] @{Area = ''
            Description                 = ''
            Resolution                  = ''
            RiskLevel                   = 10
            Resources                   = @()
        }
        Requirements                   = @{CommandAvailable = 'repadmin'
            OperatingSystem               = '*2008*'
        }
    }
    Tests                     = [ordered] @{ReplicationTests = @{Enable = $true
            Name                                                        = 'Replication Test'
            Parameters                                                  = @{ExpectedValue = 0
                Property                                              = 'Number of Failures'
                OperationType                                         = 'eq'
                PropertyExtendedValue                                 = 'Last Success Time'
                OverwriteName                                         = { "Replication from $($_.'Source DSA') to $($_.'Destination DSA'), Naming Context: $($_.'Naming Context')" }
            }
        }
    }
}
$SiteLinks = @{Enable = $true
    Source            = @{Name = 'Site Links'
        Data                   = { Get-WinADSiteLinks }
        Details                = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests             = [ordered] @{MinimalReplicationFrequency = @{Enable = $true
            Name                                                           = 'Replication Frequency should be set to maximum 60 minutes'
            Parameters                                                     = @{Property = 'ReplicationFrequencyInMinutes'
                ExpectedValue                                               = 60
                OperationType                                               = 'lt'
            }
        }
        UseNotificationsForLinks                                = @{Enable = $true
            Name                                               = 'Automatic site links should use notifications'
            Parameters                                         = @{Property = 'Options'
                ExpectedValue                            = 'UseNotify'
                OperationType                            = 'contains'
                PropertyExtendedValue                    = 'Options'
            }
        }
    }
}
$SiteLinksConnections = @{Enable = $true
    Source                       = @{Name = 'Site Links Connections'
        Data                              = { Test-ADSiteLinks -Splitter ', ' }
        Details                           = [ordered] @{Area = ''
            Description                = ''
            Resolution                 = ''
            RiskLevel                  = 10
            Resources                  = @()
        }
    }
    Tests                        = [ordered] @{AutomaticSiteLinks = @{Enable = $true
            Name                                                             = 'All site links are automatic'
            Description                                                      = 'Verify there are no manually configured sitelinks'
            Parameters                                                       = @{Property   = 'SiteLinksManualCount'
                ExpectedValue         = 0
                OperationType         = 'eq'
                PropertyExtendedValue = 'SiteLinksManual'
            }
        }
        SiteLinksNotifications                                    = @{Enable = $true
            Name                                      = 'All site links use notifications'
            Parameters                                = @{Property = 'SiteLinksNotUsingNotifyCount'
                ExpectedValue                          = 0
                OperationType                          = 'eq'
            }
        }
        SiteLinksDoNotUseNotifications                            = @{Enable = $false
            Name                                      = 'All site links are not using notifications'
            Parameters                                = @{Property = 'SiteLinksUseNotifyCount'
                ExpectedValue                                  = 0
                OperationType                                  = 'eq'
            }
        }
    }
}
$Sites = @{Enable = $true
    Source        = [ordered] @{Name = 'Sites Verification'
        Data                         = { $ADModule = Import-PrivateModule PSWinDocumentation.AD
            $Sites = & $ADModule { Get-WinADForestSites }
            [Array] $SitesWithoutDC = $Sites | Where-Object { $_.DomainControllersCount -eq 0 }
            [Array] $SitesWithoutSubnets = $Sites | Where-Object { $_.SubnetsCount -eq 0 }
            [PSCustomObject] @{SitesWithoutDC = $SitesWithoutDC.Count
                SitesWithoutSubnets           = $SitesWithoutSubnets.Count
                SitesWithoutDCName            = $SitesWithoutDC.Name -join ', '
                SitesWithoutSubnetsName       = $SitesWithoutSubnets.Name -join ', '
            } }
        Details                      = [ordered] @{Area = ''
            Description                          = ''
            Resolution                           = ''
            RiskLevel                            = 10
            Resources                            = @()
        }
    }
    Tests         = [ordered] @{SitesWithoutDC = @{Enable = $true
            Name                                          = 'Sites without Domain Controllers'
            Description                                   = 'Verify each `site has at least [one subnet configured]`'
            Parameters                                    = @{Property = 'SitesWithoutDC'
                ExpectedValue                                  = 0
                OperationType                                  = 'eq'
            }
        }
        SitesWithoutSubnets                    = @{Enable = $true
            Name                                  = 'Sites without Subnets'
            Parameters                            = @{Property = 'SitesWithoutSubnets'
                ExpectedValue                       = 0
                OperationType                       = 'eq'
            }
        }
    }
}
$TombstoneLifetime = @{Enable = $true
    Source                    = [ordered]@{Name = 'Tombstone Lifetime'
        Data                                    = { $Output = (Get-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Services,$((Get-ADRootDSE).configurationNamingContext)" -Properties tombstoneLifetime).tombstoneLifetime
            if ($null -eq $Output) { [PSCustomObject] @{TombstoneLifeTime = 60 } } else { [PSCustomObject] @{TombstoneLifeTime = $Output } } }
        Details                                 = [ordered] @{Area = ''
            Description                         = ''
            Resolution                          = ''
            RiskLevel                           = 10
            Resources                           = @()
        }
    }
    Tests                     = [ordered] @{TombstoneLifetime = @{Enable = $true
            Name                                                         = 'TombstoneLifetime should be set to minimum of 180 days'
            Parameters                                                   = @{ExpectedValue = 180
                Property                                               = 'TombstoneLifeTime'
                OperationType                                          = 'ge'
            }
        }
    }
    Resources                 = @('https://helpcenter.netwrix.com/Configure_IT_Infrastructure/AD/AD_Tombstone.html')
}
function ConvertTo-Source {
    [CmdletBinding()]
    param([string] $Source)
    if ($Source.StartsWith('Forest')) {
        $ProperSource = [ordered] @{Scope = 'Forest'
            Name                          = $Source -replace '^Forest'
        }
    } elseif ($Source.StartsWith('Domain')) {
        $ProperSource = [ordered] @{Scope = 'Domain'
            Name                          = $Source -replace '^Domain'
        }
    } elseif ($Source.StartsWith('DC')) {
        $ProperSource = [ordered] @{Scope = 'DomainControllers'
            Name                          = $Source -replace '^DC'
        }
    }
    return $ProperSource
}
function Get-TestimoDomain {
    [CmdletBinding()]
    param([string] $Domain)
    try {
        $DC = Get-ADDomainController -Discover -DomainName $Domain
        $Output = Get-ADDomain -ErrorAction Stop -Server $DC.HostName[0]
        $Output
    } catch { Out-Failure -Text "Gathering Domain Information failed. Tests for domains/domain controllers will be skipped." -Level 3 -ExtendedValue $_.Exception.Message -Type 'e' -Domain $Domain }
}
function Get-TestimoDomainControllers {
    [CmdletBinding()]
    param([string] $Domain,
        [switch] $SkipRODC)
    try {
        $DC = Get-ADDomainController -Discover -DomainName $Domain
        $DomainControllers = Get-ADDomainController -Server $DC.HostName[0] -Filter * -ErrorAction Stop
        if ($SkipRODC) { $DomainControllers = $DomainControllers | Where-Object { $_.IsReadOnly -eq $false } }
        foreach ($_ in $DomainControllers) {
            if ($Script:TestimoConfiguration['Inclusions']['DomainControllers']) {
                if ($_ -in $Script:TestimoConfiguration['Inclusions']['DomainControllers']) {
                    [PSCustomObject] @{Name = $($_.HostName).ToLower()
                        IsPDC               = $_.OperationMasterRoles -contains 'PDCEmulator'
                    }
                }
                continue
            }
            if ($_.HostName -notin $Script:TestimoConfiguration['Exclusions']['DomainControllers']) {
                [PSCustomObject] @{Name = $($_.HostName).ToLower()
                    IsPDC               = $_.OperationMasterRoles -contains 'PDCEmulator'
                }
            }
        }
    } catch { return }
}
function Get-TestimoForest {
    [CmdletBinding()]
    param([string] $ForestName)
    try {
        if ($ForestName) {
            $DC = Get-ADDomainController -Discover -DomainName $ForestName
            $Forest = Get-ADForest -ErrorAction Stop -Server $DC.HostName[0] -Identity $ForestName
        } else {
            $DC = Get-ADDomainController -Discover
            $Forest = Get-ADForest -ErrorAction Stop -Server $DC.HostName[0]
        }
        $Domains = foreach ($_ in $Forest.Domains) {
            if ($Script:TestimoConfiguration['Inclusions']['Domains']) {
                if ($_ -in $Script:TestimoConfiguration['Inclusions']['Domains']) { $_ }
                continue
            }
            if ($_ -notin $Script:TestimoConfiguration['Exclusions']['Domains']) { $_.ToLower() }
        }
        [ordered] @{Name          = $Forest.Name
            ForestMode            = $Forest.ForestMode
            Domains               = $Domains
            PartitionsContainer   = $Forest.PartitionsContainer
            DomainNamingMaster    = $Forest.DomainNamingMaster
            SchemaMaster          = $Forest.SchemaMaster
            GlobalCatalogs        = $Forest.GlobalCatalogs
            Sites                 = $Forest.Sites
            SPNSuffixes           = $Forest.SPNSuffixes
            UPNSuffixes           = $Forest.UPNSuffixes
            ApplicationPartitions = $Forest.ApplicationPartitions
            CrossForestReferences = $Forest.CrossForestReferences
        }
    } catch {
        Out-Failure -Text "Gathering Forest Information failed. Tests for domains/domain controllers will be skipped." -Level 0 -ExtendedValue $_.Exception.Message -Type 'e'
        return
    }
}
function Get-TestimoSourcesStatus {
    [cmdletbinding()]
    param([string] $Scope)
    $AllTests = foreach ($Source in $($Script:TestimoConfiguration.$Scope.Keys)) { $Script:TestimoConfiguration["$Scope"]["$Source"].Enable }
    $AllTests -contains $true
}
function Import-TestimoConfiguration {
    [CmdletBinding()]
    param([Object] $Configuration)
    if ($Configuration) {
        if ($Configuration -is [System.Collections.IDictionary]) {
            $Option = 'Hashtable'
            $LoadedConfiguration = $Configuration
        } elseif ($Configuration -is [string]) {
            if (Test-Path -LiteralPath $Configuration) {
                $Option = 'File'
                $FileContent = Get-Content -LiteralPath $Configuration
            } else {
                $Option = 'JSON'
                $FileContent = $Configuration
            }
            try { $LoadedConfiguration = $FileContent | ConvertFrom-Json } catch {
                Out-Informative -OverrideTitle 'Testimo' -Text "Loading configuration from JSON failed. Skipping." -Level 0 -Status $null -Domain $Domain -DomainController $DomainController -ExtendedValue ("Not JSON or syntax is incorrect.")
                return
            }
        } else { Out-Informative -OverrideTitle 'Testimo' -Text "Loading configuratio failed. Skipping." -Level 0 -Status $null -Domain $Domain -DomainController $DomainController -ExtendedValue ("Not JSON/Hashtable or syntax is incorrect.") }
        Out-Informative -OverrideTitle 'Testimo' -Text "Using configuration provided by user" -Level 0 -Start
        $Scopes = 'Forest', 'Domain', 'DomainControllers'
        foreach ($Scope in $Scopes) {
            if ($LoadedConfiguration -is [System.Collections.IDictionary]) {
                foreach ($Key in ($LoadedConfiguration.$Scope).Keys) {
                    $Script:TestimoConfiguration[$Scope][$Key]['Enable'] = $LoadedConfiguration.$Scope.$Key.Enable
                    if ($null -ne $LoadedConfiguration[$Scope][$Key]['Source']) {
                        if ($null -ne $LoadedConfiguration[$Scope][$Key]['Source']['ExpectedOutput']) { $Script:TestimoConfiguration[$Scope][$Key]['Source']['ExpectedOutput'] = $LoadedConfiguration.$Scope.$Key['Source']['ExpectedOutput'] }
                        if ($null -ne $LoadedConfiguration[$Scope][$Key]['Source']['Parameters']) { foreach ($Parameter in [string] $LoadedConfiguration[$Scope][$Key]['Source']['Parameters'].Keys) { $Script:TestimoConfiguration[$Scope][$Key]['Source']['Parameters'][$Parameter] = $LoadedConfiguration[$Scope][$Key]['Source']['Parameters'][$Parameter] } }
                    }
                    foreach ($Test in $LoadedConfiguration.$Scope.$Key.Tests.Keys) {
                        $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Enable'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Enable
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedValue) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['ExpectedValue'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedValue }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedCount) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['ExpectedCount'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedCount }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.Property) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['Property'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.Property }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.OperationType) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['OperationType'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.OperationType }
                    }
                }
            } else {
                foreach ($Key in ($LoadedConfiguration.$Scope).PSObject.Properties.Name) {
                    $Script:TestimoConfiguration[$Scope][$Key]['Enable'] = $LoadedConfiguration.$Scope.$Key.Enable
                    if ($null -ne $LoadedConfiguration.$Scope.$Key.'Source') {
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.'Source'.'ExpectedOutput') { $Script:TestimoConfiguration[$Scope][$Key]['Source']['ExpectedOutput'] = $LoadedConfiguration.$Scope.$Key.'Source'.'ExpectedOutput' }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.'Source'.'Parameters') { foreach ($Parameter in $LoadedConfiguration.$Scope.$Key.'Source'.'Parameters'.PSObject.Properties.Name) { $Script:TestimoConfiguration[$Scope][$Key]['Source']['Parameters'][$Parameter] = $LoadedConfiguration.$Scope.$Key.'Source'.'Parameters'.$Parameter } }
                    }
                    foreach ($Test in $LoadedConfiguration.$Scope.$Key.Tests.PSObject.Properties.Name) {
                        $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Enable'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Enable
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedValue) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['ExpectedValue'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedValue }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedCount) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['ExpectedCount'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.ExpectedCount }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.Property) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['Property'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.Property }
                        if ($null -ne $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.OperationType) { $Script:TestimoConfiguration[$Scope][$Key]['Tests'][$Test]['Parameters']['OperationType'] = $LoadedConfiguration.$Scope.$Key.Tests.$Test.Parameters.OperationType }
                    }
                }
            }
        }
        Out-Informative -OverrideTitle 'Testimo' -Status $null -Domain $Domain -DomainController $DomainController -ExtendedValue ("Configuration loaded from $Option") -End
    } else { Out-Informative -OverrideTitle 'Testimo' -Text "Using configuration defaults" -Level 0 -Status $null -ExtendedValue ("No configuration provided by user") }
}
function Out-Begin {
    [CmdletBinding()]
    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 } elseif ($Type -eq 'e') { [ConsoleColor[]] $Color = [ConsoleColor]::Red, [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 } elseif ($Type -eq 'e') { [ConsoleColor[]] $Color = [ConsoleColor]::Red, [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 } elseif ($Type -eq 'e') { [ConsoleColor[]] $Color = [ConsoleColor]::Red, [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 {
    [CmdletBinding()]
    param([string] $Text,
        [int] $Level,
        [string] $ExtendedValue = 'Input data not provided. Failing test.',
        [string] $Domain,
        [string] $DomainController,
        [string] $ReferenceID,
        [validateSet('e', 'i', 't')][string] $Type = 't')
    Out-Begin -Text $Text -Level $Level -Domain $Domain -DomainController $DomainController -Type $Type
    Out-Status -Text $Text -Status $false -ExtendedValue $ExtendedValue -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
}
function Out-Informative {
    [CmdletBinding()]
    param([int] $Level = 0,
        [string] $OverrideTitle,
        [string] $Domain,
        [string] $DomainController,
        [string] $Text,
        [nullable[bool]] $Status,
        [string] $ExtendedValue,
        [switch] $Start,
        [switch] $End)
    if ($Start -or (-not $Start -and -not $End)) {
        $Type = 'i'
        if ($Domain -and $DomainController) {
            [ConsoleColor[]] $Color = [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow, [ConsoleColor]::Yellow
            $TestText = "[$Type]", "[$Domain]", "[$($DomainController)] ", $Text
        } elseif ($Domain) {
            [ConsoleColor[]] $Color = [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow
            $TestText = "[$Type]", "[$Domain] ", $Text
        } elseif ($DomainController) { Write-Warning "Out-Begin - Shouldn't happen - Fix me." } else {
            [ConsoleColor[]] $Color = [ConsoleColor]::Yellow, [ConsoleColor]::DarkGray, [ConsoleColor]::Yellow
            if ($OverrideTitle) { $TestText = "[$Type]", "[$OverrideTitle] ", $Text } else { $TestText = "[$Type]", "[Forest] ", $Text }
        }
        Write-Color -Text $TestText -Color $Color -StartSpaces $Level -NoNewLine
    }
    if ($End -or (-not $Start -and -not $End)) {
        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]::Magenta, [ConsoleColor]::Cyan
        }
        if ($ExtendedValue) { Write-Color -Text ' [', $TextStatus, ']', " [", $ExtendedValue, "]" -Color $Color } else { Write-Color -Text ' [', $TextStatus, ']' -Color $Color }
    }
}
function Out-Skip {
    [CmdletBinding()]
    param([PSCustomobject] $TestsSummary,
        [int] $Level = 0,
        [string] $Domain,
        [string] $DomainController,
        [string] $Test,
        [string] $Source,
        [string] $Reason = 'Skipping - unmet dependency')
    Out-Begin -Type 'i' -Text $Test -Level $Level -Domain $Domain -DomainController $DomainController
    Out-Status -Text $Test -Status $null -ExtendedValue $Reason -Domain $Domain -DomainController $DomainController -ReferenceID $Source
    $TestsSummary.Skipped = $TestsSummary.Skipped + 1
    $TestsSummary.Total = $TestsSummary.Failed + $TestsSummary.Passed + $TestsSummary.Skipped
    $TestsSummary
}
function Out-Status {
    [CmdletBinding()]
    param([string] $TestID,
        [string] $Text,
        [nullable[bool]] $Status,
        [string] $Section,
        [string] $ExtendedValue,
        [string] $Domain,
        [string] $DomainController,
        [System.Collections.IDictionary] $SourceDetails,
        [System.Collections.IDictionary] $TestDetails,
        [string] $ReferenceID)
    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]::Magenta, [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'
        $TestText = "Forest | $Text"
    }
    $Output = [PSCustomObject]@{Name = $TestText
        Type                         = $TestType
        Status                       = $Status
        Extended                     = $ExtendedValue
        Domain                       = $Domain
        DomainController             = $DomainController
    }
    if (-not $ReferenceID) { $Script:Reporting['Errors'].Add($Output) } else { if ($Domain -and $DomainController) { $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DomainController]['Tests'][$ReferenceID]['Results'].Add($Output) } elseif ($Domain) { $Script:Reporting['Domains'][$Domain]['Tests'][$ReferenceID]['Results'].Add($Output) } else { $Script:Reporting['Forest']['Tests'][$ReferenceID]['Results'].Add($Output) } }
    $Script:TestResults.Add($Output)
}
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
}
function Set-TestsStatus {
    [CmdletBinding()]
    param([string[]] $Sources,
        [string[]] $ExcludeSources)
    if ($Sources) {
        $Scopes = @('Forest', 'Domain', 'DomainControllers')
        foreach ($Scope in $Scopes) { foreach ($Test in $Script:TestimoConfiguration.$Scope.Keys) { $Script:TestimoConfiguration.$Scope[$Test]['Enable'] = $false } }
        foreach ($Source in $Sources) {
            if ($Source.StartsWith('Forest')) {
                $ProperSource = $Source -replace '^Forest'
                $Script:TestimoConfiguration['Forest'][$ProperSource]['Enable'] = $true
            } elseif ($Source.StartsWith('Domain')) {
                $ProperSource = $Source -replace '^Domain'
                $Script:TestimoConfiguration['Domain'][$ProperSource]['Enable'] = $true
            } elseif ($Source.StartsWith('DC')) {
                $ProperSource = $Source -replace '^DC'
                $Script:TestimoConfiguration['DomainControllers'][$ProperSource]['Enable'] = $true
            }
        }
    }
    foreach ($Source in $ExcludeSources) {
        if ($Source.StartsWith('Forest')) {
            $ProperSource = $Source -replace '^Forest'
            $Script:TestimoConfiguration['Forest'][$ProperSource]['Enable'] = $false
        } elseif ($Source.StartsWith('Domain')) {
            $ProperSource = $Source -replace '^Domain'
            $Script:TestimoConfiguration['Domain'][$ProperSource]['Enable'] = $false
        } elseif ($Source.StartsWith('DC')) {
            $ProperSource = $Source -replace '^DC'
            $Script:TestimoConfiguration['DomainControllers'][$ProperSource]['Enable'] = $false
        }
    }
}
function Start-TestimoEmail {
    [CmdletBinding()]
    param([string] $From,
        [string[]] $To,
        [string[]] $CC,
        [string[]] $BCC,
        [string] $Server,
        [int] $Port,
        [switch] $SSL,
        [string] $UserName,
        [string] $Password,
        [switch] $PasswordAsSecure,
        [switch] $PasswordFromFile,
        [string] $Priority = 'High',
        [string] $Subject = '[Reporting Evotec] Summary of Active Directory Tests')
    Email { EmailHeader { EmailFrom -Address $From
            EmailTo -Addresses $To
            EmailServer -Server $Server -UserName $UserName -Password $PasswordFromFile -PasswordAsSecure:$PasswordAsSecure -PasswordFromFile:$PasswordFromFile -Port 587 -SSL:$SSL
            EmailOptions -Priority $Priority -DeliveryNotifications Never
            EmailSubject -Subject $Subject }
        EmailBody -FontFamily 'Calibri' -Size 15 { EmailTable -DataTable $Results { EmailTableCondition -ComparisonType 'string' -Name 'Status' -Operator eq -Value 'True' -BackgroundColor Green -Color White -Inline -Row
                EmailTableCondition -ComparisonType 'string' -Name 'Status' -Operator ne -Value 'True' -BackgroundColor Red -Color White -Inline -Row } -HideFooter } } -AttachSelf -Supress $false
}
function Start-TestimoReport {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $TestResults,
        [string] $FilePath,
        [switch] $UseCssLinks,
        [switch] $UseJavaScriptLinks,
        [switch] $ShowHTML)
    if ($FilePath -eq '') { $FilePath = Get-FileName -Extension 'html' -Temporary }
    $ColorPassed = 'LawnGreen'
    $ColorSkipped = 'DeepSkyBlue'
    $ColorFailed = 'Tomato'
    $ColorPassedText = 'Black'
    $ColorFailedText = 'Black'
    $ColorSkippedText = 'Black'
    [Array] $PassedTests = $TestResults['Results'] | Where-Object { $_.Status -eq $true }
    [Array] $FailedTests = $TestResults['Results'] | Where-Object { $_.Status -eq $false }
    [Array] $SkippedTests = $TestResults['Results'] | Where-Object { $_.Status -ne $true -and $_.Status -ne $false }
    New-HTML -FilePath $FilePath -UseCssLinks:$UseCssLinks -UseJavaScriptLinks:$UseJavaScriptLinks { New-HTMLHeader { New-HTMLSection -Invisible { New-HTMLSection { New-HTMLText -Text "Report generated on $(Get-Date)" -Color Blue } -JustifyContent flex-start -Invisible
                New-HTMLSection { New-HTMLText -Text $Script:Reporting['Version'] -Color Blue } -JustifyContent flex-end -Invisible } }
        New-HTMLTab -Name 'Summary' -IconBrands galactic-senate { New-HTMLSection -HeaderText "Tests results" -HeaderBackGroundColor DarkGray { New-HTMLPanel { New-HTMLChart { New-ChartPie -Name 'Passed' -Value ($PassedTests.Count) -Color $ColorPassed
                        New-ChartPie -Name 'Failed' -Value ($FailedTests.Count) -Color $ColorFailed
                        New-ChartPie -Name 'Skipped' -Value ($SkippedTests.Count) -Color $ColorSkipped }
                    New-HTMLTable -DataTable $TestResults['Summary'] -HideFooter -DisableSearch { New-HTMLTableContent -ColumnName 'Passed' -BackGroundColor $ColorPassed -Color $ColorPassedText
                        New-HTMLTableContent -ColumnName 'Failed' -BackGroundColor $ColorFailed -Color $ColorFailedText
                        New-HTMLTableContent -ColumnName 'Skipped' -BackGroundColor $ColorSkipped -Color $ColorSkippedText } }
                New-HTMLPanel { New-HTMLTable -DataTable $TestResults['Results'] { New-HTMLTableCondition -Name 'Status' -Value $true -BackgroundColor $ColorPassed -Color $ColorPassedText
                        New-HTMLTableCondition -Name 'Status' -Value $false -BackgroundColor $ColorFailed -Color $ColorFailedText
                        New-HTMLTableCondition -Name 'Status' -Value $null -BackgroundColor $ColorSkipped -Color $ColorSkippedText } } } }
        New-HTMLTab -Name 'Forest' -IconBrands first-order { foreach ($Source in $TestResults['Forest']['Tests'].Keys) {
                $Name = $TestResults['Forest']['Tests'][$Source]['Name']
                $Data = $TestResults['Forest']['Tests'][$Source]['Data']
                $SourceCode = $TestResults['Forest']['Tests'][$Source]['SourceCode']
                $Results = $TestResults['Forest']['Tests'][$Source]['Results']
                [Array] $PassedTestsSingular = $TestResults['Forest']['Tests'][$Source]['Results'] | Where-Object { $_.Status -eq $true }
                [Array] $FailedTestsSingular = $TestResults['Forest']['Tests'][$Source]['Results'] | Where-Object { $_.Status -eq $false }
                [Array] $SkippedTestsSingular = $TestResults['Forest']['Tests'][$Source]['Results'] | Where-Object { $_.Status -ne $true -and $_.Status -ne $false }
                New-HTMLSection -HeaderText $Name -HeaderBackGroundColor DarkGray { New-HTMLContainer { New-HTMLPanel { New-HTMLChart { New-ChartPie -Name 'Passed' -Value ($PassedTestsSingular.Count) -Color $ColorPassed
                                New-ChartPie -Name 'Failed' -Value ($FailedTestsSingular.Count) -Color $ColorFailed
                                New-ChartPie -Name 'Skipped' -Value ($SkippedTestsSingular.Count) -Color $ColorSkipped }
                            New-HTMLCodeBlock -Code $SourceCode -Style 'PowerShell' -Theme enlighter } }
                    New-HTMLContainer { New-HTMLPanel { New-HTMLTable -DataTable $Data
                            New-HTMLTable -DataTable $Results { New-HTMLTableCondition -Name 'Status' -Value $true -BackgroundColor $ColorPassed -Color $ColorPassedText
                                New-HTMLTableCondition -Name 'Status' -Value $false -BackgroundColor $ColorFailed -Color $ColorFailedText
                                New-HTMLTableCondition -Name 'Status' -Value $null -BackgroundColor $ColorSkipped -Color $ColorSkippedText } } } }
            } }
        foreach ($Domain in $TestResults['Domains'].Keys) {
            New-HTMLTab -Name "Domain $Domain" -IconBrands deskpro { foreach ($Source in $TestResults['Domains'][$Domain]['Tests'].Keys) {
                    $Name = $TestResults['Domains'][$Domain]['Tests'][$Source]['Name']
                    $Data = $TestResults['Domains'][$Domain]['Tests'][$Source]['Data']
                    $SourceCode = $TestResults['Domains'][$Domain]['Tests'][$Source]['SourceCode']
                    $Results = $TestResults['Domains'][$Domain]['Tests'][$Source]['Results']
                    [Array] $PassedTestsSingular = $TestResults['Domains'][$Domain]['Tests'][$Source]['Results'] | Where-Object { $_.Status -eq $true }
                    [Array] $FailedTestsSingular = $TestResults['Domains'][$Domain]['Tests'][$Source]['Results'] | Where-Object { $_.Status -eq $false }
                    [Array] $SkippedTestsSingular = $TestResults['Domains'][$Domain]['Tests'][$Source]['Results'] | Where-Object { $_.Status -ne $true -and $_.Status -ne $false }
                    New-HTMLSection -HeaderText $Name -HeaderBackGroundColor DarkGray { New-HTMLContainer { New-HTMLPanel { New-HTMLChart { New-ChartPie -Name 'Passed' -Value ($PassedTestsSingular.Count) -Color $ColorPassed
                                    New-ChartPie -Name 'Failed' -Value ($FailedTestsSingular.Count) -Color $ColorFailed
                                    New-ChartPie -Name 'Skipped' -Value ($SkippedTestsSingular.Count) -Color $ColorSkipped }
                                New-HTMLCodeBlock -Code $SourceCode -Style 'PowerShell' -Theme enlighter } }
                        New-HTMLContainer { New-HTMLPanel { New-HTMLTable -DataTable $Data
                                New-HTMLTable -DataTable $Results { New-HTMLTableCondition -Name 'Status' -Value $true -BackgroundColor $ColorPassed -Color $ColorPassedText
                                    New-HTMLTableCondition -Name 'Status' -Value $false -BackgroundColor $ColorFailed -Color $ColorFailedText
                                    New-HTMLTableCondition -Name 'Status' -Value $null -BackgroundColor $ColorSkipped -Color $ColorSkippedText } } } }
                }
                foreach ($DC in $TestResults['Domains'][$Domain]['DomainControllers'].Keys) {
                    New-HTMLSection -HeaderText "Domain Controller - $DC" -HeaderBackGroundColor DarkSlateGray { New-HTMLContainer { foreach ($Source in $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'].Keys) {
                                $Name = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['Name']
                                $Data = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['Data']
                                $SourceCode = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['SourceCode']
                                $Results = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['Results']
                                [Array] $PassedTestsSingular = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['Results'] | Where-Object { $_.Status -eq $true }
                                [Array] $FailedTestsSingular = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['Results'] | Where-Object { $_.Status -eq $false }
                                [Array] $SkippedTestsSingular = $TestResults['Domains'][$Domain]['DomainControllers'][$DC]['Tests'][$Source]['Results'] | Where-Object { $_.Status -ne $true -and $_.Status -ne $false }
                                New-HTMLSection -HeaderText $Name -HeaderBackGroundColor DarkGray { New-HTMLContainer { New-HTMLPanel { New-HTMLChart { New-ChartPie -Name 'Passed' -Value ($PassedTestsSingular.Count) -Color $ColorPassed
                                                New-ChartPie -Name 'Failed' -Value ($FailedTestsSingular.Count) -Color $ColorFailed
                                                New-ChartPie -Name 'Skipped' -Value ($SkippedTestsSingular.Count) -Color $ColorSkipped }
                                            New-HTMLCodeBlock -Code $SourceCode -Style 'PowerShell' -Theme enlighter } }
                                    New-HTMLContainer { New-HTMLPanel { New-HTMLTable -DataTable $Data
                                            New-HTMLTable -DataTable $Results { New-HTMLTableCondition -Name 'Status' -Value $true -BackgroundColor $ColorPassed -Color $ColorPassedText
                                                New-HTMLTableCondition -Name 'Status' -Value $false -BackgroundColor $ColorFailed -Color $ColorFailedText
                                                New-HTMLTableCondition -Name 'Status' -Value $null -BackgroundColor $ColorSkipped -Color $ColorSkippedText } } } }
                            } } }
                } }
        } } -ShowHTML:$ShowHTML
}
function Start-Testing {
    [CmdletBinding()]
    param([ScriptBlock] $Execute,
        [string] $Scope,
        [string] $Domain,
        [string] $DomainController,
        [bool] $IsPDC,
        [Object] $ForestInformation,
        [Object] $DomainInformation)
    $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" }
    [bool] $IsDomainRoot = $ForestInformation.Name -eq $Domain
    Out-Informative -Text $SummaryText -Status $null -ExtendedValue '' -Domain $Domain -DomainController $DomainController -Level ($LevelSummary - 3)
    $TestsSummaryTogether = @(foreach ($Source in $($Script:TestimoConfiguration.$Scope.Keys)) {
            $CurrentSection = $Script:TestimoConfiguration.$Scope[$Source]
            if ($null -eq $CurrentSection) {
                Write-Warning "Source $Source in scope: $Scope is defined improperly. Please verify."
                continue
            }
            if ($CurrentSection['Enable'] -eq $true) {
                $Time = Start-TimeLog
                $CurrentSource = $CurrentSection['Source']
                [Array] $AllTests = $CurrentSection['Tests'].Keys
                $ReferenceID = $Source
                $TestsSummary = [PSCustomobject] @{Passed = 0
                    Failed                                = 0
                    Skipped                               = 0
                    Total                                 = 0
                }
                if ($Domain -and $DomainController) {
                    $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DomainController]['Tests'][$ReferenceID] = [ordered] @{Name = $CurrentSource['Name']
                        SourceCode                                                                                                          = $CurrentSource['Data']
                        Details                                                                                                             = $CurrentSource['Details']
                        Results                                                                                                             = [System.Collections.Generic.List[PSCustomObject]]::new()
                        Domain                                                                                                              = $Domain
                        DomainController                                                                                                    = $DomainController
                    }
                } elseif ($Domain) {
                    $Script:Reporting['Domains'][$Domain]['Tests'][$ReferenceID] = [ordered] @{Name = $CurrentSource['Name']
                        SourceCode                                                                  = $CurrentSource['Data']
                        Details                                                                     = $CurrentSource['Details']
                        Results                                                                     = [System.Collections.Generic.List[PSCustomObject]]::new()
                        Domain                                                                      = $Domain
                        DomainController                                                            = $DomainController
                    }
                } else {
                    $Script:Reporting['Forest']['Tests'][$ReferenceID] = [ordered] @{Name = $CurrentSource['Name']
                        SourceCode                                                        = $CurrentSource['Data']
                        Details                                                           = $CurrentSource['Details']
                        Results                                                           = [System.Collections.Generic.List[PSCustomObject]]::new()
                        Domain                                                            = $Domain
                        DomainController                                                  = $DomainController
                    }
                }
                if (-not $CurrentSection['Source']) {
                    Write-Warning "Source $Source in scope: $Scope is defined improperly. Please verify."
                    continue
                }
                if ($CurrentSource['Requirements']) {
                    if ($null -ne $CurrentSource['Requirements']['IsDomainRoot']) {
                        if (-not $CurrentSource['Requirements']['IsDomainRoot'] -eq $IsDomainRoot) {
                            Out-Skip -Test $CurrentSource['Name'] -DomainController $DomainController -Domain $Domain -TestsSummary $TestsSummary -Source $ReferenceID -Level $Level
                            continue
                        }
                    }
                    if ($null -ne $CurrentSource['Requirements']['IsPDC']) {
                        if (-not $CurrentSource['Requirements']['IsPDC'] -eq $IsPDC) {
                            Out-Skip -Test $CurrentSource['Name'] -DomainController $DomainController -Domain $Domain -TestsSummary $TestsSummary -Source $ReferenceID -Level $Level
                            continue
                        }
                    }
                    if ($null -ne $CurrentSource['Requirements']['OperatingSystem']) { }
                    if ($null -ne $CurrentSource['Requirements']['CommandAvailable']) {
                        [Array] $Commands = foreach ($Command in $CurrentSource['Requirements']['CommandAvailable']) {
                            $OutputCommand = Get-Command -Name $Command -ErrorAction SilentlyContinue
                            if (-not $OutputCommand) { $false }
                        }
                        if ($Commands -contains $false) {
                            $CommandsTested = $CurrentSource['Requirements']['CommandAvailable'] -join ', '
                            Out-Skip -Test $CurrentSource['Name'] -DomainController $DomainController -Domain $Domain -TestsSummary $TestsSummary -Source $ReferenceID -Level $Level -Reason "Skipping - At least one command unavailable ($CommandsTested)"
                            continue
                        }
                    }
                }
                if ($CurrentSource['Parameters']) {
                    $SourceParameters = $CurrentSource['Parameters']
                    $Object = Start-TestProcessing -Test $CurrentSource['Name'] -Level $Level -OutputRequired -Domain $Domain -DomainController $DomainController { & $CurrentSource['Data'] @SourceParameters -DomainController $DomainController -Domain $Domain }
                } else { $Object = Start-TestProcessing -Test $CurrentSource['Name'] -Level $Level -OutputRequired -Domain $Domain -DomainController $DomainController { & $CurrentSource['Data'] -DomainController $DomainController -Domain $Domain } }
                if ($Domain -and $DomainController) { $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DomainController]['Tests'][$ReferenceID]['Data'] = $Object } elseif ($Domain) { $Script:Reporting['Domains'][$Domain]['Tests'][$ReferenceID]['Data'] = $Object } else { $Script:Reporting['Forest']['Tests'][$ReferenceID]['Data'] = $Object }
                if ($Object -and ($null -eq $CurrentSource['ExpectedOutput'] -or $CurrentSource['ExpectedOutput'] -eq $true)) {
                    $FailAllTests = $false
                    Out-Begin -Text $CurrentSource['Name'] -Level $LevelTest -Domain $Domain -DomainController $DomainController
                    Out-Status -Text $CurrentSource['Name'] -Status $true -ExtendedValue 'Data is available.' -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
                    $TestsSummary.Passed = $TestsSummary.Passed + 1
                } elseif ($Object -and $CurrentSource['ExpectedOutput'] -eq $false) {
                    $FailAllTests = $true
                    Out-Failure -Text $CurrentSource['Name'] -Level $LevelTest -ExtendedValue 'Data is available. This is a bad thing.' -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
                    $TestsSummary.Failed = $TestsSummary.Failed + 1
                } elseif ($null -eq $Object -and $CurrentSource['ExpectedOutput'] -eq $false) {
                    $FailAllTests = $false
                    Out-Begin -Text $CurrentSource['Name'] -Level $LevelTest -Domain $Domain -DomainController $DomainController
                    Out-Status -Text $CurrentSource['Name'] -Status $true -ExtendedValue 'No data returned, which is a good thing.' -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
                    $TestsSummary.Passed = $TestsSummary.Passed + 1
                } else {
                    $FailAllTests = $true
                    Out-Failure -Text $CurrentSource['Name'] -Level $LevelTest -ExtendedValue 'No data available.' -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
                    $TestsSummary.Failed = $TestsSummary.Failed + 1
                }
                foreach ($Test in $AllTests) {
                    $CurrentTest = $CurrentSection['Tests'][$Test]
                    if ($CurrentTest['Enable'] -eq $True) {
                        if ($CurrentTest['Requirements']) {
                            if ($null -ne $CurrentTest['Requirements']['IsDomainRoot']) {
                                if (-not $CurrentTest['Requirements']['IsDomainRoot'] -eq $IsDomainRoot) {
                                    $TestsSummary.Skipped = $TestsSummary.Skipped + 1
                                    continue
                                }
                            }
                            if ($null -ne $CurrentTest['Requirements']['IsPDC']) {
                                if (-not $CurrentTest['Requirements']['IsPDC'] -eq $IsPDC) {
                                    $TestsSummary.Skipped = $TestsSummary.Skipped + 1
                                    continue
                                }
                            }
                        }
                        if (-not $FailAllTests) {
                            if ($CurrentTest['Parameters']) { $Parameters = $CurrentTest['Parameters'] } else { $Parameters = $null }
                            $TestsResults = Start-TestingTest -Test $CurrentTest['Name'] -Level $LevelTest -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID { Test-StepOne -Object $Object -Domain $Domain -DomainController $DomainController @Parameters -Level $LevelTest -TestName $CurrentTest['Name'] -ReferenceID $ReferenceID }
                            $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['Name'] -Level $LevelTestFailure -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
                        }
                    } else { $TestsSummary.Skipped = $TestsSummary.Skipped + 1 }
                }
                $TestsSummary.Total = $TestsSummary.Failed + $TestsSummary.Passed + $TestsSummary.Skipped
                $TestsSummary
                Out-Summary -Text $CurrentSource['Name'] -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
    if ($Domain -and $DomainController) { $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DomainController]['Summary'] = $TestsSummaryFinal } elseif ($Domain) { $Script:Reporting['Domains'][$Domain]['Summary'] = $TestsSummaryFinal } else { $Script:Reporting['Summary'] = $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,
        [string] $ReferenceID)
    if ($Execute) {
        if ($Script:TestimoConfiguration.Debug.ShowErrors) {
            [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 -ReferenceID $ReferenceID }
        }
    }
}
function Start-TestProcessing {
    [CmdletBinding()]
    param([ScriptBlock] $Execute,
        [string] $Test,
        [switch] $OutputRequired,
        [nullable[bool]] $ExpectedStatus,
        [int] $Level = 0,
        [switch] $Simple,
        [string] $Domain,
        [string] $DomainController,
        [string] $ReferenceID)
    if ($Execute) {
        Out-Informative -Text $Test -Level $Level -Domain $Domain -DomainController $DomainController -Start
        if ($Script:TestimoConfiguration.Debug.ShowErrors) {
            [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-Informative -Text $Test -Status $TestResult -ExtendedValue $O.Extended -Domain $Domain -DomainController $DomainController -End
        } else { Out-Informative -Text $Test -Status $TestResult -ExtendedValue $ErrorMessage -Domain $Domain -DomainController $DomainController -End }
    }
}
function Test-StepOne {
    [CmdletBinding()]
    param([string] $Domain,
        [string] $DomainController,
        [Array] $Object,
        [string] $TestName,
        [string] $OperationType,
        [int] $Level,
        [string[]] $Property,
        [string[]] $PropertyExtendedValue,
        [Object] $ExpectedValue,
        [int] $ExpectedCount,
        [string] $OperationResult,
        [string] $ReferenceID,
        [nullable[bool]] $ExpectedOutput,
        [scriptblock] $WhereObject,
        [scriptblock] $OverwriteName)
    if ($OperationType -eq '') { $OperationType = 'eq' }
    if ($Object) {
        if ($WhereObject) { $Object = $Object | Where-Object $WhereObject }
        if ($null -eq $Object) {
            Out-Begin -Text $TestName -Level $Level -Domain $Domain -DomainController $DomainController
            Out-Status -Text $TestName -Status $false -ExtendedValue 'Data is not available.' -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
            return $false
        }
        if ($ExpectedCount) {
            if ($OverwriteName) { $TestName = & $OverwriteName }
            Test-StepTwo -Object $Object -ExpectedCount $ExpectedCount -OperationType $OperationType -TestName $TestName -Level $Level -Domain $Domain -DomainController $DomainController -Property $Property -ExpectedValue $ExpectedValue -PropertyExtendedValue $PropertyExtendedValue -OperationResult $OperationResult -ReferenceID $ReferenceID -ExpectedOutput $ExpectedOutput
        } else {
            foreach ($_ in $Object) {
                if ($OverwriteName) { $TestName = & $OverwriteName }
                Test-StepTwo -Object $_ -OperationType $OperationType -TestName $TestName -Level $Level -Domain $Domain -DomainController $DomainController -Property $Property -ExpectedValue $ExpectedValue -PropertyExtendedValue $PropertyExtendedValue -OperationResult $OperationResult -ReferenceID $ReferenceID -ExpectedOutput $ExpectedOutput
            }
        }
    }
}
function Test-StepTwo {
    [CmdletBinding()]
    param([string] $Domain,
        [string] $DomainController,
        [Array] $Object,
        [string] $TestName,
        [string] $OperationType,
        [int] $Level,
        [string[]] $Property,
        [string[]] $PropertyExtendedValue,
        [Array] $ExpectedValue,
        [int] $ExpectedCount = -1,
        [string] $OperationResult,
        [string] $ReferenceID,
        [nullable[bool]] $ExpectedOutput)
    Out-Begin -Text $TestName -Level $Level -Domain $Domain -DomainController $DomainController
    $ScriptBlock = { $Operators = @{'lt' = 'LessThan'
            'gt'                         = 'GreaterThan'
            'le'                         = 'LessOrEqual'
            'ge'                         = 'GreaterOrEqual'
            'eq'                         = 'Equal'
            'contains'                   = 'Contains'
            'notcontains'                = 'Not contains'
            'like'                       = 'Like'
            'match'                      = 'Match'
            'notmatch'                   = 'Not match'
        }
        [Object] $TestedValue = $Object
        foreach ($V in $Property) { $TestedValue = $TestedValue.$V }
        if ($ExpectedCount -ne -1) {
            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 } elseif ($OperationType -eq 'like') { $TestResult = $Object.Count -like $ExpectedCount } elseif ($OperationType -eq 'contains') { $TestResult = $Object.Count -like $ExpectedCount } elseif ($OperationType -eq 'in') { $TestResult = $ExpectedCount -in $Object.Count } elseif ($OperationType -eq 'notin') { $TestResult = $ExpectedCount -notin $Object.Count } else { $TestResult = $Object.Count -eq $ExpectedCount }
            $TextTestedValue = $Object.Count
            $TextExpectedValue = $ExpectedCount
        } elseif ($null -ne $ExpectedValue) {
            $OutputValues = [System.Collections.Generic.List[Object]]::new()
            if ($null -eq $TestedValue -and $null -ne $ExpectedValue) {
                $TestResult = for ($i = 0; $i -lt $ExpectedValue.Count; $i++) {
                    $false
                    if ($ExpectedValue[$i] -is [string] -and $ExpectedValue[$i] -like '*Get-Date*') {
                        [scriptblock] $DateConversion = [scriptblock]::Create($ExpectedValue[$i])
                        $CompareValue = & $DateConversion
                    } else { $CompareValue = $ExpectedValue[$I] }
                    $OutputValues.Add($CompareValue)
                }
                $TextExpectedValue = $OutputValues -join ', '
                $TextTestedValue = 'Null'
            } else {
                [Array] $TestResult = @(if ($OperationType -eq 'notin') {
                        $ExpectedValue -notin $TestedValue
                        $TextExpectedValue = $ExpectedValue
                    } elseif ($OperationType -eq 'in') {
                        $ExpectedValue -in $TestedValue
                        $TextExpectedValue = $ExpectedValue
                    } else {
                        for ($i = 0; $i -lt $ExpectedValue.Count; $i++) {
                            if ($ExpectedValue[$i] -is [string] -and $ExpectedValue[$i] -like '*Get-Date*') {
                                [scriptblock] $DateConversion = [scriptblock]::Create($ExpectedValue[$i])
                                $CompareValue = & $DateConversion
                            } else { $CompareValue = $ExpectedValue[$I] }
                            if ($OperationType -eq 'lt') { $TestedValue -lt $CompareValue } elseif ($OperationType -eq 'gt') { $TestedValue -gt $CompareValue } elseif ($OperationType -eq 'ge') { $TestedValue -ge $CompareValue } elseif ($OperationType -eq 'le') { $TestedValue -le $CompareValue } elseif ($OperationType -eq 'like') { $TestedValue -like $CompareValue } elseif ($OperationType -eq 'contains') { $TestedValue -contains $CompareValue } elseif ($OperationType -eq 'notcontains') { $TestedValue -notcontains $CompareValue } elseif ($OperationType -eq 'match') { $TestedValue -match $CompareValue } elseif ($OperationType -eq 'notmatch') { $TestedValue -notmatch $CompareValue } else { $TestedValue -eq $CompareValue }
                            $OutputValues.Add($CompareValue)
                        }
                        $TextExpectedValue = $OutputValues -join ', '
                    }
                    $TextTestedValue = $TestedValue)
            }
        } else {
            if ($ExpectedOutput -eq $false) {
                [Array] $TestResult = @(if ($null -eq $TestedValue) { $true } else { $false })
                $TextExpectedValue = 'No output'
            } else {
                $TestResult = $null
                $ExtendedTextValue = "Test provided but no tests required."
            }
        }
        if ($null -eq $TestResult) {
            $ReportResult = $null
            $ReportExtended = $ExtendedTextValue
        } else {
            if ($OperationResult -eq 'OR') {
                if ($TestResult -contains $true) {
                    $ReportResult = $true
                    $ReportExtended = "Expected value ($($Operators[$OperationType])): $($TextExpectedValue)"
                } else {
                    $ReportResult = $false
                    $ReportExtended = "Expected value ($($Operators[$OperationType])): $TextExpectedValue, Found value: $($TextTestedValue)"
                }
            } else {
                if ($TestResult -notcontains $false) {
                    $ReportResult = $true
                    $ReportExtended = "Expected value ($($Operators[$OperationType])): $($TextExpectedValue)"
                } else {
                    $ReportResult = $false
                    $ReportExtended = "Expected value ($($Operators[$OperationType])): $TextExpectedValue, Found value: $($TextTestedValue)"
                }
            }
        }
        if ($PropertyExtendedValue.Count -gt 0) {
            $ReportExtended = $Object
            foreach ($V in $PropertyExtendedValue) { $ReportExtended = $ReportExtended.$V }
            $ReportExtended = $ReportExtended -join ', '
        }
        Out-Status -Text $TestName -Status $ReportResult -ExtendedValue $ReportExtended -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
        return $ReportResult }
    if ($Script:TestimoConfiguration.Debug.ShowErrors) { & $ScriptBlock } else {
        try { & $ScriptBlock } catch {
            Out-Status -Text $TestName -Status $false -ExtendedValue $_.Exception.Message -Domain $Domain -DomainController $DomainController -ReferenceID $ReferenceID
            return $False
        }
    }
}
$Script:TestimoConfiguration = [ordered] @{Exclusions = [ordered] @{Domains = @()
        DomainControllers                                                   = @()
    }
    Inclusions                                        = [ordered] @{Domains = @()
        DomainControllers                                                   = @()
    }
    Forest                                            = [ordered]@{Backup = $Backup
        Replication                                                       = $Replication
        ReplicationStatus                                                 = $ReplicationStatus
        OptionalFeatures                                                  = $OptionalFeatures
        Sites                                                             = $Sites
        SiteLinks                                                         = $SiteLinks
        SiteLinksConnections                                              = $SiteLinksConnections
        Roles                                                             = $ForestFSMORoles
        OrphanedAdmins                                                    = $OrphanedAdmins
        ObjectsWithConflict                                               = $ObjectsWithConflict
        TombstoneLifetime                                                 = $TombstoneLifetime
    }
    Domain                                            = [ordered] @{Roles = $DomainFSMORoles
        WellKnownFolders                                                  = $WellKnownFolders
        PasswordComplexity                                                = $PasswordComplexity
        GroupPolicyMissingPermissions                                     = $GroupPolicyMissingPermissions
        Trusts                                                            = $Trusts
        OrphanedForeignSecurityPrincipals                                 = $OrphanedForeignSecurityPrincipals
        EmptyOrganizationalUnits                                          = $EmptyOrganizationalUnits
        DNSScavengingForPrimaryDNSServer                                  = $DNSScavengingForPrimaryDNSServer
        DNSForwaders                                                      = $DNSForwaders
        DnsZonesAging                                                     = $DnsZonesAging
        KerberosAccountAge                                                = $KerberosAccountAge
        SecurityGroupsAccountOperators                                    = $SecurityGroupsAccountOperators
        SecurityGroupsSchemaAdmins                                        = $SecurityGroupsSchemaAdmins
        SecurityUsersAcccountAdministrator                                = $SecurityUsersAcccountAdministrator
        SysVolDFSR                                                        = $SysVolDFSR
        'DNSZonesForest0ADEL'                                             = $DNSZonesForest0ADEL
        'DNSZonesDomain0ADEL'                                             = $DNSZonesDomain0ADEL
        DHCPAuthorized                                                    = $DHCPAuthorized
        GroupPolicyADM                                                    = $GroupPolicyADM
        ComputersUnsupported                                              = $ComputersUnsupported
        ComputersUnsupportedMainstream                                    = $ComputersUnsupportedMainstream
    }
    DomainControllers                                 = [ordered] @{Information = $Information
        WindowsRemoteManagement                                                 = $WindowsRemoteManagement
        EventLogs                                                               = $EventLogs
        OperatingSystem                                                         = $OperatingSystem
        Services                                                                = $Services
        LDAP                                                                    = $LDAP
        LDAPInsecureBindings                                                    = $LDAPInsecureBindings
        Pingable                                                                = $Pingable
        Ports                                                                   = $Ports
        RDPPorts                                                                = $RDPPorts
        RDPSecurity                                                             = $RDPSecurity
        DiskSpace                                                               = $DiskSpace
        TimeSettings                                                            = $TimeSettings
        TimeSynchronizationInternal                                             = $TimeSynchronizationInternal
        TimeSynchronizationExternal                                             = $TimeSynchronizationExternal
        NetworkCardSettings                                                     = $NetworkCardSettings
        WindowsUpdates                                                          = $WindowsUpdates
        WindowsRolesAndFeatures                                                 = $WindowsRolesAndFeatures
        DnsResolveInternal                                                      = $DNSResolveInternal
        DnsResolveExternal                                                      = $DNSResolveExternal
        DnsNameServes                                                           = $DNSNameServers
        SMBProtocols                                                            = $SMBProtocols
        SMBShares                                                               = $SMBShares
        DFS                                                                     = $DFS
        NTDSParameters                                                          = $NTDSParameters
        GroupPolicySYSVOL                                                       = $GroupPolicySYSVOL
        LanManagerSettings                                                      = $LanManagerSettings
        Diagnostics                                                             = $Diagnostics
        LanManServer                                                            = $LanManServer
        MSSLegacy                                                               = $MSSLegacy
        FileSystem                                                              = $FileSystem
        NetSessionEnumaration                                                   = $NetSessionEnumaration
        ServiceWINRM                                                            = $ServiceWINRM
    }
    Debug                                             = [ordered] @{ShowErrors = $false }
}
function Get-TestimoConfiguration {
    [CmdletBinding()]
    param([switch] $AsJson,
        [string] $FilePath)
    $NewConfig = [ordered] @{ }
    $Scopes = 'Forest', 'Domain', 'DomainControllers'
    foreach ($Scope in $Scopes) {
        $NewConfig[$Scope] = [ordered] @{ }
        foreach ($Source in ($Script:TestimoConfiguration[$Scope]).Keys) {
            $NewConfig[$Scope][$Source] = [ordered] @{ }
            $NewConfig[$Scope][$Source]['Enable'] = $Script:TestimoConfiguration[$Scope][$Source]['Enable']
            if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Source']['ExpectedOutput']) {
                $NewConfig[$Scope][$Source]['Source'] = [ordered] @{ }
                $NewConfig[$Scope][$Source]['Source']['ExpectedOutput'] = $Script:TestimoConfiguration[$Scope][$Source]['Source']['ExpectedOutput']
            }
            if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Source']['Parameters']) {
                $NewConfig[$Scope][$Source]['Source'] = [ordered] @{ }
                $NewConfig[$Scope][$Source]['Source']['Parameters'] = $Script:TestimoConfiguration[$Scope][$Source]['Source']['Parameters']
            }
            $NewConfig[$Scope][$Source]['Tests'] = [ordered] @{ }
            foreach ($Test in $Script:TestimoConfiguration[$Scope][$Source]['Tests'].Keys) {
                $NewConfig[$Scope][$Source]['Tests'][$Test] = [ordered] @{ }
                $NewConfig[$Scope][$Source]['Tests'][$Test]['Enable'] = $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Enable']
                $NewConfig[$Scope][$Source]['Tests'][$Test]['Parameters'] = [ordered] @{ }
                if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']) {
                    if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['Property']) {
                        if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['Property']) { $NewConfig[$Scope][$Source]['Tests'][$Test]['Parameters']['Property'] = $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['Property'] }
                        if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['ExpectedValue']) { $NewConfig[$Scope][$Source]['Tests'][$Test]['Parameters']['ExpectedValue'] = $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['ExpectedValue'] }
                        if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['ExpectedCount']) { $NewConfig[$Scope][$Source]['Tests'][$Test]['Parameters']['ExpectedCount'] = $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['ExpectedCount'] }
                        if ($null -ne $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['OperationType']) { $NewConfig[$Scope][$Source]['Tests'][$Test]['Parameters']['OperationType'] = $Script:TestimoConfiguration[$Scope][$Source]['Tests'][$Test]['Parameters']['OperationType'] }
                    }
                }
            }
        }
    }
    if ($FilePath) {
        $NewConfig | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $FilePath
        return
    }
    if ($AsJSON) { return $NewConfig | ConvertTo-Json -Depth 10 }
    return $NewConfig
}
function Get-TestimoSources {
    [CmdletBinding()]
    param([string[]] $Source,
        [switch] $Enabled)
    if ($Source) {
        $DetectedSource = ConvertTo-Source -Source $Source
        $Scope = $DetectedSource.Scope
        $Name = $DetectedSource.Name
        $Script:TestimoConfiguration.$Scope[$Name].Tests.Keys
    } else {
        $ForestKeys = $TestimoConfiguration.Forest.Keys
        $DomainKeys = $TestimoConfiguration.Domain.Keys
        $DomainControllerKeys = $TestimoConfiguration.DomainControllers.Keys
        $TestSources = @(foreach ($Key in $ForestKeys) { if ($Enabled) { if ($TestimoConfiguration.Forest["$Key"].Enable) { "Forest$Key" } } else { "Forest$Key" } }
            foreach ($Key in $DomainKeys) { if ($Enabled) { if ($TestimoConfiguration.Domain["$Key"].Enable) { "Domain$Key" } } else { "Domain$Key" } }
            foreach ($Key in $DomainControllerKeys) { if ($Enabled) { if ($TestimoConfiguration.DomainControllers["$Key"].Enable) { "DC$Key" } } else { "DC$Key" } })
        $TestSources | Sort-Object
    }
}
function Import-PrivateModule {
    [cmdletBinding()]
    param([string] $Name,
        [switch] $Portable)
    try { $ADModule = Import-Module -PassThru -Name $Name -ErrorAction Stop } catch {
        if ($_.Exception.Message -like '*was not loaded because no valid module file was found in any module directory*') {
            $Module = Get-Module -Name $Name
            if ($Module) { $ADModule = Import-Module $Module -PassThru }
        }
    }
    $ADModule
}
function Invoke-Testimo {
    [alias('Test-ImoAD', 'Test-IMO')]
    [CmdletBinding()]
    param([ValidateScript( { $_ -in (& $SourcesAutoCompleter) })]
        [string[]] $Sources,
        [ValidateScript( { $_ -in (& $SourcesAutoCompleter) })] [string[]] $ExcludeSources,
        [string[]] $ExcludeDomains,
        [string[]] $ExcludeDomainControllers,
        [string[]] $IncludeDomains,
        [string[]] $IncludeDomainControllers,
        [switch] $ReturnResults,
        [switch] $ShowErrors,
        [switch] $ExtendedResults,
        [Object] $Configuration,
        [string] $ReportPath,
        [switch] $ShowReport,
        [switch] $SkipRODC)
    if (-not $Script:DefaultSources) { $Script:DefaultSources = Get-TestimoSources -Enabled } else { Set-TestsStatus -Sources $Script:DefaultSources }
    $Script:Reporting = [ordered] @{ }
    $Script:Reporting['Version'] = ''
    $Script:Reporting['Errors'] = [System.Collections.Generic.List[PSCustomObject]]::new()
    $Script:Reporting['Results'] = $null
    $Script:Reporting['Summary'] = $null
    $Script:Reporting['Forest'] = [ordered] @{ }
    $Script:Reporting['Forest']['Summary'] = $null
    $Script:Reporting['Forest']['Tests'] = [ordered] @{ }
    $Script:Reporting['Domains'] = [ordered] @{ }
    $TestimoVersion = Get-Command -Name 'Invoke-Testimo' -ErrorAction SilentlyContinue
    [Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/evotecit/Testimo/releases")
    $LatestVersion = $GitHubReleases[0]
    if (-not $LatestVersion.Errors) { if ($TestimoVersion.Version -eq $LatestVersion.Version) { $Script:Reporting['Version'] = "Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)" } elseif ($TestimoVersion.Version -lt $LatestVersion.Version) { $Script:Reporting['Version'] = "Current: $($TestimoVersion.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?" } elseif ($TestimoVersion.Version -gt $LatestVersion.Version) { $Script:Reporting['Version'] = "Current: $($TestimoVersion.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!" } } else { $Script:Reporting['Version'] = "Current: $($TestimoVersion.Version)" }
    Out-Informative -OverrideTitle 'Testimo' -Text 'Version' -Level 0 -Status $null -ExtendedValue $Script:Reporting['Version']
    Import-TestimoConfiguration -Configuration $Configuration
    $global:ProgressPreference = 'SilentlyContinue'
    $global:ErrorActionPreference = 'Stop'
    $Script:TestResults = [System.Collections.Generic.List[PSCustomObject]]::new()
    $Script:TestimoConfiguration.Debug.ShowErrors = $ShowErrors
    $Script:TestimoConfiguration.Exclusions.Domains = $ExcludeDomains
    $Script:TestimoConfiguration.Exclusions.DomainControllers = $ExcludeDomainControllers
    $Script:TestimoConfiguration.Inclusions.Domains = $IncludeDomains
    $Script:TestimoConfiguration.Inclusions.DomainControllers = $IncludeDomainControllers
    Set-TestsStatus -Sources $Sources -ExcludeSources $ExcludeSources
    if ($Script:TestimoConfiguration.Inclusions.Domains) { Out-Informative -Text 'Only following Domains will be scanned' -Level 0 -Status $null -ExtendedValue ($Script:TestimoConfiguration.Exclusions.Domains -join ', ') }
    if ($Script:TestimoConfiguration.Inclusions.DomainControllers) { Out-Informative -Text 'Only following Domain Controllers will be scanned' -Level 0 -Status $null -ExtendedValue ($Script:TestimoConfiguration.Exclusions.DomainControllers -join ', ') }
    if ($Script:TestimoConfiguration.Exclusions.Domains -and -not $Script:TestimoConfiguration.Inclusions.Domains) { Out-Informative -Text 'Following Domains will be ignored' -Level 0 -Status $null -ExtendedValue ($Script:TestimoConfiguration.Exclusions.Domains -join ', ') }
    if ($Script:TestimoConfiguration.Exclusions.DomainControllers -and -not $Script:TestimoConfiguration.Inclusions.DomainControllers) { Out-Informative -Text 'Following Domain Controllers will be ignored' -Level 0 -Status $null -ExtendedValue ($Script:TestimoConfiguration.Exclusions.DomainControllers -join ', ') }
    $ForestInformation = Get-TestimoForest -ForestName $ForestName
    $null = Start-Testing -Scope 'Forest' -ForestInformation $ForestInformation { foreach ($Domain in $ForestInformation.Domains) {
            $Script:Reporting['Domains'][$Domain] = [ordered] @{ }
            $Script:Reporting['Domains'][$Domain]['Summary'] = $null
            $Script:Reporting['Domains'][$Domain]['Tests'] = [ordered] @{ }
            $Script:Reporting['Domains'][$Domain]['DomainControllers'] = [ordered] @{ }
            $DomainInformation = Get-TestimoDomain -Domain $Domain
            if ($DomainInformation) {
                Start-Testing -Scope 'Domain' -Domain $Domain -DomainInformation $DomainInformation -ForestInformation $ForestInformation { if (Get-TestimoSourcesStatus -Scope 'DomainControllers') {
                        $DomainControllers = Get-TestimoDomainControllers -Domain $Domain -SkipRODC:$SkipRODC
                        foreach ($DC in $DomainControllers) {
                            $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DC.Name] = [ordered] @{ }
                            $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DC.Name]['Summary'] = $null
                            $Script:Reporting['Domains'][$Domain]['DomainControllers'][$DC.Name]['Tests'] = [ordered] @{ }
                            Start-Testing -Scope 'DomainControllers' -Domain $Domain -DomainController $DC.Name -IsPDC $DC.IsPDC -DomainInformation $DomainInformation -ForestInformation $ForestInformation
                        }
                    } }
            }
        } }
    $Script:Reporting['Results'] = $Script:TestResults
    if ($ReturnResults -and $ExtendedResults) { $Script:Reporting } else { if ($ReturnResults) { $Script:TestResults } }
    if ($ReportPath -or $ShowReport) { Start-TestimoReport -FilePath $ReportPath -UseCssLinks:$true -UseJavaScriptLinks:$true -ShowHTML:$ShowReport -TestResults $Script:Reporting }
}
[scriptblock] $SourcesAutoCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $ForestKeys = $Script:TestimoConfiguration.Forest.Keys
    $DomainKeys = $Script:TestimoConfiguration.Domain.Keys
    $DomainControllerKeys = $Script:TestimoConfiguration.DomainControllers.Keys
    $TestSources = @(foreach ($Key in $ForestKeys) { "Forest$Key" }
        foreach ($Key in $DomainKeys) { "Domain$Key" }
        foreach ($Key in $DomainControllerKeys) { "DC$Key" })
    $TestSources | Sort-Object }
Register-ArgumentCompleter -CommandName Invoke-Testimo -ParameterName Sources -ScriptBlock $SourcesAutoCompleter
Register-ArgumentCompleter -CommandName Invoke-Testimo -ParameterName ExcludeSources -ScriptBlock $SourcesAutoCompleter
Export-ModuleMember -Function @('Get-TestimoConfiguration', 'Get-TestimoSources', 'Import-PrivateModule', 'Invoke-Testimo') -Alias @('Test-IMO', 'Test-ImoAD')