PSWinDocumentation.AD.psm1

Add-Type -TypeDefinition @"
    using System;
 
    namespace PSWinDocumentation
    {
        [Flags]
        public enum ActiveDirectory {
            // Forest Information - Section Main
            ForestInformation,
            ForestFSMO,
            ForestGlobalCatalogs,
            ForestOptionalFeatures,
            ForestUPNSuffixes,
            ForestSPNSuffixes,
            ForestSites,
            ForestSites1,
            ForestSites2,
            ForestSubnets,
            ForestSubnets1,
            ForestSubnets2,
            ForestSiteLinks,
            ForestDomainControllers,
            ForestRootDSE,
            ForestSchemaPropertiesUsers,
            ForestSchemaPropertiesComputers,
 
            // Domain Information - Section Main
            DomainRootDSE,
            DomainRIDs,
            DomainAuthenticationPolicies, // Not yet tested
            DomainAuthenticationPolicySilos, // Not yet tested
            DomainCentralAccessPolicies, // Not yet tested
            DomainCentralAccessRules, // Not yet tested
            DomainClaimTransformPolicies, // Not yet tested
            DomainClaimTypes, // Not yet tested
            DomainFineGrainedPolicies,
            DomainFineGrainedPoliciesUsers,
            DomainFineGrainedPoliciesUsersExtended,
            DomainGUIDS,
            DomainDNSSRV,
            DomainDNSA,
            DomainInformation,
            DomainControllers,
            DomainFSMO,
            DomainDefaultPasswordPolicy,
            DomainGroupPolicies,
            DomainGroupPoliciesDetails,
            DomainGroupPoliciesACL,
            DomainOrganizationalUnits,
            DomainOrganizationalUnitsBasicACL,
            DomainOrganizationalUnitsExtendedACL,
            DomainContainers,
            DomainTrustsClean,
            DomainTrusts,
 
            DomainBitlocker,
            DomainLAPS,
 
            // Domain Information - Group Data
            DomainGroupsFullList, // Contains all data
 
            DomainGroups,
            DomainGroupsMembers,
            DomainGroupsMembersRecursive,
 
            DomainGroupsSpecial,
            DomainGroupsSpecialMembers,
            DomainGroupsSpecialMembersRecursive,
 
            DomainGroupsPriviliged,
            DomainGroupsPriviligedMembers,
            DomainGroupsPriviligedMembersRecursive,
 
            // Domain Information - User Data
            DomainUsersFullList, // Contains all data
            DomainUsers,
            DomainUsersCount,
            DomainUsersAll,
            DomainUsersSystemAccounts,
            DomainUsersNeverExpiring,
            DomainUsersNeverExpiringInclDisabled,
            DomainUsersExpiredInclDisabled,
            DomainUsersExpiredExclDisabled,
            DomainAdministrators,
            DomainAdministratorsRecursive,
            DomainEnterpriseAdministrators,
            DomainEnterpriseAdministratorsRecursive,
 
            // Domain Information - Computer Data
            DomainComputersFullList, // Contains all data
            DomainComputersAll,
            DomainComputersAllCount,
            DomainComputers,
            DomainComputersCount,
            DomainServers,
            DomainServersCount,
            DomainComputersUnknown,
            DomainComputersUnknownCount,
 
            // This requires DSInstall PowerShell Module
            DomainPasswordDataUsers, // Gathers users data and their passwords
            DomainPasswordDataPasswords, // Compares Users Password with File
            DomainPasswordDataPasswordsHashes, // Compares Users Password with File HASH
            DomainPasswordClearTextPassword, // include both enabled / disabled accounts
            DomainPasswordClearTextPasswordEnabled, // include only enabled
            DomainPasswordClearTextPasswordDisabled, // include only disabled
            DomainPasswordLMHash,
            DomainPasswordEmptyPassword,
            DomainPasswordWeakPassword,
            DomainPasswordWeakPasswordEnabled,
            DomainPasswordWeakPasswordDisabled,
            DomainPasswordWeakPasswordList, // Password List from file..
            DomainPasswordDefaultComputerPassword,
            DomainPasswordPasswordNotRequired,
            DomainPasswordPasswordNeverExpires,
            DomainPasswordAESKeysMissing,
            DomainPasswordPreAuthNotRequired,
            DomainPasswordDESEncryptionOnly,
            DomainPasswordDelegatableAdmins,
            DomainPasswordDuplicatePasswordGroups,
            DomainPasswordHashesWeakPassword,
            DomainPasswordHashesWeakPasswordEnabled,
            DomainPasswordHashesWeakPasswordDisabled,
            DomainPasswordStats
        }
    }
"@

function Clear-DataInformation {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Data,
        [Array] $TypesRequired,
        [switch] $DontRemoveSupportData,
        [switch] $DontRemoveEmpty)
    foreach ($Domain in $Data.FoundDomains.Keys) {
        $RemoveDomainKeys = foreach ($Key in $Data.FoundDomains.$Domain.Keys) {
            if ($null -eq $Data.FoundDomains.$Domain.$Key) {
                if (-not $DontRemoveEmpty) {$Key}
                continue
            }
            if ($Key -notin $TypesRequired -and $DontRemoveSupportData -eq $false) {$Key}
        }
        foreach ($Key in $RemoveDomainKeys) {$Data.FoundDomains.$Domain.Remove($Key)}
    }
    $RemoveDomains = foreach ($Domain in $Data.FoundDomains.Keys) {if ($Data.FoundDomains.$Domain.Count -eq 0) {$Domain}}
    foreach ($Domain in $RemoveDomains) {$Data.FoundDomains.Remove($Domain)}
    if ($Data.FoundDomains.Count -eq 0) {$Data.Remove('FoundDomains')}
    $RemoveKeys = foreach ($Key in $Data.Keys) {
        if ($Key -eq 'FoundDomains') {continue}
        if ($null -eq $Data.$Key) {
            if (-not $DontRemoveEmpty) {$Key}
            continue
        }
        if ($Key -notin $TypesRequired -and $DontRemoveSupportData -eq $false) {$Key}
    }
    foreach ($Key in $RemoveKeys) {$Data.Remove($Key)}
}
function Get-ADObjectFromDistingusishedName {
    [CmdletBinding()]
    param ([string[]] $DistinguishedName,
        [Object[]] $ADCatalog,
        [string] $Type = '',
        [string] $Splitter)
    if ($null -eq $DistinguishedName) {return}
    $FoundObjects = foreach ($Catalog in $ADCatalog) {foreach ($Object in $DistinguishedName) {foreach ($_ in $Catalog) {if ($_.DistinguishedName -eq $Object) {if ($Type -eq '') {$_} else {$_.$Type}}}}}
    if ($Splitter) {return ($FoundObjects | Sort-Object) -join $Splitter} else {return $FoundObjects | Sort-Object}
}
function Get-ADObjectFromDNHash {
    [CmdletBinding()]
    param ([string[]] $DistinguishedName,
        [hashtable] $ADCatalog,
        [string] $Type = '',
        [string] $Splitter)
    if ($null -eq $DistinguishedName) {return}
    $FoundObjects = foreach ($DN in $DistinguishedName) {if ($Type -eq '') {$ADCatalog.$DN} else {$ADCatalog.$DN.$Type}}
    if ($Splitter) {return ($FoundObjects | Sort-Object) -join $Splitter} else {return $FoundObjects | Sort-Object}
}
function Get-DataInformation {
    [CmdletBinding()]
    param([ScriptBlock] $Content,
        [string] $Text,
        [Array] $TypesRequired,
        [Array] $TypesNeeded)
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded $TypesNeeded) {
        Write-Verbose -Message $Text
        $Time = Start-TimeLog
        if ($null -ne $Content) {& $Content}
        $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
        Write-Verbose "$Text - Time: $EndTime"
    }
}
function Get-WinADAccounts {
    [CmdletBinding()]
    param([Array] $UserNameList,
        [Array[]] $ADCatalog)
    $Accounts = foreach ($User in $UserNameList) {foreach ($Catalog in $ADCatalog) {foreach ($_ in $Catalog) {if ($_.SamAccountName -eq $User) {$_}}}}
    return $Accounts
}
function Get-WinADDomain {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    try {Get-ADDomain -Server $Domain -ErrorAction Stop} catch {$null}
}
function Get-WinADDomainAdministrators {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembers,
        $DomainInformation)
    $DomainGroupsMembers | Where-Object {$_.'Group SID' -eq $('{0}-512' -f $DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
}
function Get-WinADDomainAdministratorsRecursive {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembersRecursive,
        $DomainInformation)
    $DomainGroupsMembersRecursive | Where-Object {$_.'Group SID' -eq $('{0}-512' -f $DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
}
function Get-WinADDomainAllUsersCount {
    [CmdletBinding()]
    param([Array] $DomainUsers,
        [Array] $DomainUsersAll,
        [Array] $DomainUsersExpiredExclDisabled,
        [Array] $DomainUsersExpiredInclDisabled,
        [Array] $DomainUsersNeverExpiring,
        [Array] $DomainUsersNeverExpiringInclDisabled,
        [Array] $DomainUsersSystemAccounts)
    $DomainUsersCount = [ordered] @{'Users Count Incl. System' = $DomainUsers.Count
        'Users Count' = $DomainUsersAll.Count
        'Users Expired' = $DomainUsersExpiredExclDisabled.Count
        'Users Expired Incl. Disabled' = $DomainUsersExpiredInclDisabled.Count
        'Users Never Expiring' = $DomainUsersNeverExpiring.Count
        'Users Never Expiring Incl. Disabled' = $DomainUsersNeverExpiringInclDisabled.Count
        'Users System Accounts' = $DomainUsersSystemAccounts.Count
    }
    return $DomainUsersCount
}
function Get-WinADDomainBitlocker {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $Computers)
    $Properties = @('Name',
        'OperatingSystem',
        'DistinguishedName')
    if ($null -eq $Computers) {$Computers = Get-ADComputer -Filter * -Properties $Properties -Server $Domain}
    foreach ($Computer in $Computers) {
        try {$Bitlockers = Get-ADObject -Filter 'objectClass -eq "msFVE-RecoveryInformation"' -SearchBase $Computer.DistinguishedName -Properties 'WhenCreated', 'msFVE-RecoveryPassword'} catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like "*The supplied distinguishedName must belong to one of the following partition(s)*") {Write-Warning "Getting domain information - $Domain - Couldn't get Bitlocker information. Most likely not enabled."} else {Write-Warning "Getting domain information - $Domain - Couldn't get Bitlocker information. Error: $ErrorMessage"}
            return
        }
        foreach ($Bitlocker in $Bitlockers) {
            [PSCustomObject] @{'Name' = $Computer.Name
                'Operating System' = $Computer.'OperatingSystem'
                'Bitlocker Recovery Password' = $Bitlocker.'msFVE-RecoveryPassword'
                'Bitlocker When' = $Bitlocker.WhenCreated
                'DistinguishedName' = $Computer.'DistinguishedName'
            }
        }
    }
}
function Get-WinADDomainComputers {
    [CmdletBinding()]
    param([Array] $DomainComputersAll)
    foreach ($_ in $DomainComputersAll) {if ($_.OperatingSystem -notlike 'Windows Server*' -and $null -ne $_.OperatingSystem) {$_}}
}
function Get-WinADDomainComputersAll {
    [CmdletBinding()]
    param([Array] $DomainComputersFullList,
        [string] $Splitter,
        [hashtable] $DomainObjects)
    [DateTime] $CurrentDate = Get-Date
    foreach ($_ in $DomainComputersFullList) {
        $Manager = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $_.ManagedBy
        [PSCustomObject] @{SamAccountName = $_.SamAccountName
            Enabled = $_.Enabled
            OperatingSystem = $_.OperatingSystem
            PasswordLastSet = $_.PasswordLastSet
            'PasswordLastChanged(Days)' = if ($null -ne $_.PasswordLastSet) {"$(-$($_.PasswordLastSet - $CurrentDate).Days)"} else {}
            IPv4Address = $_.IPv4Address
            IPv6Address = $_.IPv6Address
            Name = $_.Name
            DNSHostName = $_.DNSHostName
            'Manager' = $Manager.Name
            'ManagerEmail' = if ($Splitter -ne '') {$Manager.EmailAddress -join $Splitter} else {$Manager.EmailAddress}
            OperatingSystemVersion = $_.OperatingSystemVersion
            OperatingSystemHotfix = $_.OperatingSystemHotfix
            OperatingSystemServicePack = $_.OperatingSystemServicePack
            PasswordNeverExpires = $_.PasswordNeverExpires
            PasswordNotRequired = $_.PasswordNotRequired
            UserPrincipalName = $_.UserPrincipalName
            LastLogonDate = $_.LastLogonDate
            'LastLogonDate(Days)' = if ($null -ne $_.LastLogonDate) {"$(-$($_.LastLogonDate - $CurrentDate).Days)"} else {}
            LockedOut = $_.LockedOut
            LogonCount = $_.LogonCount
            CanonicalName = $_.CanonicalName
            SID = $_.SID
            Created = $_.Created
            Modified = $_.Modified
            Deleted = $_.Deleted
            "Protected" = $_.ProtectedFromAccidentalDeletion
            "PrimaryGroup" = (Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $_.PrimaryGroup -Type 'SamAccountName')
            "MemberOf" = (Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $_.MemberOf -Type 'SamAccountName' -Splitter $Splitter)
        }
    }
}
function Get-WinADDomainComputersAllCount {
    [CmdletBinding()]
    param([Array] $DomainComputersAll)
    $DomainComputersAll | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'System Count'; Expression = {$_.Count}}
}
function Get-WinADDomainComputersCount {
    [CmdletBinding()]
    param([Array] $DomainComputers)
    $DomainComputers | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'N/A'}}} , @{L = 'System Count'; Expression = {$_.Count}}
}
function Get-WinADDomainComputersFullList {
    [cmdletbinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $ForestSchemaComputers,
        [HashTable] $DomainObjects,
        [int] $ResultPageSize = 500000)
    if ($Extended) {[string] $Properties = '*'} else {
        [string[]] $Properties = @('SamAccountName', 'Enabled', 'OperatingSystem',
            'PasswordLastSet', 'IPv4Address', 'IPv6Address', 'Name', 'DNSHostName',
            'ManagedBy', 'OperatingSystemVersion', 'OperatingSystemHotfix',
            'OperatingSystemServicePack' , 'PasswordNeverExpires',
            'PasswordNotRequired', 'UserPrincipalName',
            'LastLogonDate', 'LockedOut', 'LogonCount',
            'CanonicalName', 'SID', 'Created', 'Modified',
            'Deleted', 'MemberOf', 'PrimaryGroup', 'ProtectedFromAccidentalDeletion'
            if ($ForestSchemaComputers.Name -contains 'ms-Mcs-AdmPwd') {
                'ms-Mcs-AdmPwd'
                'ms-Mcs-AdmPwdExpirationTime'
            })
    }
    $Computers = Get-ADComputer -Server $Domain -Filter * -ResultPageSize $ResultPageSize -Properties $Properties -ErrorAction SilentlyContinue
    foreach ($_ in $Computers) {$DomainObjects.$($_.DistinguishedName) = $_}
    $Computers
}
function Get-WinADDomainComputersUnknown {
    [CmdletBinding()]
    param([Array] $DomainComputersAll)
    foreach ($_ in $DomainComputersAll) {if ($null -eq $_.OperatingSystem) {$_}}
}
function Get-WinADDomainComputersUnknownCount {
    [CmdletBinding()]
    param([Array] $DomainComputersUnknown)
    $DomainComputersUnknown | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'System Count'; Expression = {$_.Count}}
}
function Get-WinADDomainControllersInternal {
    param([string] $Domain)
    $DomainControllersClean = Get-ADDomainController -Server $Domain -Filter *
    foreach ($DC in $DomainControllersClean) {
        [PSCustomObject][ordered] @{'Name' = $DC.Name
            'Host Name' = $DC.HostName
            'Operating System' = $DC.OperatingSystem
            'Site' = $DC.Site
            'Ipv4' = $DC.Ipv4Address
            'Ipv6' = $DC.Ipv6Address
            'Global Catalog?' = $DC.IsGlobalCatalog
            'Read Only?' = $DC.IsReadOnly
            'Ldap Port' = $DC.LdapPort
            'SSL Port' = $DC.SSLPort
        }
    }
}
function Get-WinADDomainDefaultPasswordPolicy {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    $Policy = Get-ADDefaultDomainPasswordPolicy -Server $Domain
    [ordered] @{'Complexity Enabled' = $Policy.ComplexityEnabled
        'Lockout Duration' = $Policy.LockoutDuration
        'Lockout Observation Window' = $Policy.LockoutObservationWindow
        'Lockout Threshold' = $Policy.LockoutThreshold
        'Max Password Age' = $Policy.MaxPasswordAge
        'Min Password Length' = $Policy.MinPasswordLength
        'Min Password Age' = $Policy.MinPasswordAge
        'Password History Count' = $Policy.PasswordHistoryCount
        'Reversible Encryption Enabled' = $Policy.ReversibleEncryptionEnabled
        'Distinguished Name' = $Policy.DistinguishedName
    }
}
function Get-WinADDomainDNSData {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    $DnsRecords = "_kerberos._tcp.$Domain", "_ldap._tcp.$Domain"
    $DNSData = foreach ($DnsRecord in $DnsRecords) {
        $Value = Resolve-DnsName -Name $DnsRecord -Type SRV -Verbose:$false -ErrorAction SilentlyContinue
        if ($null -eq $Value) {Write-Warning 'Getting domain information - DomainDNSSRV / DomainDNSA - Failed!'}
        $Value
    }
    $ReturnData = @{}
    $ReturnData.Srv = foreach ($V in $DNSData) {if ($V.QueryType -eq 'SRV') {$V | Select-Object Target, NameTarget, Priority, Weight, Port, Name}}
    $ReturnData.A = foreach ($V in $DNSData) {if ($V.QueryType -ne 'SRV') {$V | Select-Object Address, IPAddress, IP4Address, Name, Type, DataLength, TTL}}
    return $ReturnData
}
function Get-WinADDomainEnterpriseAdministrators {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembers,
        $DomainInformation)
    $DomainGroupsMembers | Where-Object {$_.'Group SID' -eq $('{0}-519' -f $DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
}
function Get-WinADDomainEnterpriseAdministratorsRecursive {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembersRecursive,
        $DomainInformation)
    $DomainGroupsMembersRecursive | Where-Object {$_.'Group SID' -eq $('{0}-519' -f $DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
}
function Get-WinADDomainFineGrainedPolicies {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    $FineGrainedPoliciesData = Get-ADFineGrainedPasswordPolicy -Filter * -Server $Domain
    $FineGrainedPolicies = foreach ($Policy in $FineGrainedPoliciesData) {
        [PSCustomObject][ordered] @{'Name' = $Policy.Name
            'Complexity Enabled' = $Policy.ComplexityEnabled
            'Lockout Duration' = $Policy.LockoutDuration
            'Lockout Observation Window' = $Policy.LockoutObservationWindow
            'Lockout Threshold' = $Policy.LockoutThreshold
            'Max Password Age' = $Policy.MaxPasswordAge
            'Min Password Length' = $Policy.MinPasswordLength
            'Min Password Age' = $Policy.MinPasswordAge
            'Password History Count' = $Policy.PasswordHistoryCount
            'Reversible Encryption Enabled' = $Policy.ReversibleEncryptionEnabled
            'Precedence' = $Policy.Precedence
            'Applies To' = $Policy.AppliesTo
            'Distinguished Name' = $Policy.DistinguishedName
        }
    }
    return $FineGrainedPolicies
}
function Get-WinADDomainFineGrainedPoliciesUsers {
    [CmdletBinding()]
    param([Array] $DomainFineGrainedPolicies,
        [hashtable] $DomainObjects)
    $PolicyUsers = foreach ($Policy in $DomainFineGrainedPolicies) {
        $AllObjects = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $U}
        foreach ($_ in $AllObjects) {
            [pscustomobject][ordered] @{'Policy Name' = $Policy.Name
                Name = $_.Name
                SamAccountName = $_.SamAccountName
                Type = $_.ObjectClass
                SID = $_.SID
            }
        }
    }
    return $PolicyUsers
}
function Get-WinADDomainFineGrainedPoliciesUsersExtended {
    [CmdletBinding()]
    param([Array] $DomainFineGrainedPolicies,
        [Array] $DomainUsersFullList,
        [Array] $DomainGroupsFullList,
        [string] $Domain = ($Env:USERDNSDOMAIN).ToLower())
    $PolicyUsers = @(foreach ($Policy in $DomainFineGrainedPolicies) {
            $Users = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $U}
            foreach ($User in $Users) {
                [pscustomobject][ordered] @{'Policy Name' = $Policy.Name
                    Name = $User.Name
                    SamAccountName = $User.SamAccountName
                    Type = $User.ObjectClass
                    SID = $User.SID
                    'High Privileged Group' = 'N/A'
                    'Display Name' = $User.DisplayName
                    'Member Name' = $Member.Name
                    'User Principal Name' = $User.UserPrincipalName
                    'Sam Account Name' = $User.SamAccountName
                    'Email Address' = $User.EmailAddress
                    'PasswordExpired' = $User.PasswordExpired
                    'PasswordLastSet' = $User.PasswordLastSet
                    'PasswordNotRequired' = $User.PasswordNotRequired
                    'PasswordNeverExpires' = $User.PasswordNeverExpires
                    'Enabled' = $User.Enabled
                    'MemberSID' = $Member.SID.Value
                    'Manager' = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $User.Manager).Name
                    'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $User.Manager).EmailAddress
                    'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")
                    "DaysToExpire" = (Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring $($User."msDS-UserPasswordExpiryTimeComputed")))
                    "AccountExpirationDate" = $User.AccountExpirationDate
                    "AccountLockoutTime" = $User.AccountLockoutTime
                    "AllowReversiblePasswordEncryption" = $User.AllowReversiblePasswordEncryption
                    "BadLogonCount" = $User.BadLogonCount
                    "CannotChangePassword" = $User.CannotChangePassword
                    "CanonicalName" = $User.CanonicalName
                    'Given Name' = $User.GivenName
                    'Surname' = $User.Surname
                    "Description" = $User.Description
                    "DistinguishedName" = $User.DistinguishedName
                    "EmployeeID" = $User.EmployeeID
                    "EmployeeNumber" = $User.EmployeeNumber
                    "LastBadPasswordAttempt" = $User.LastBadPasswordAttempt
                    "LastLogonDate" = $User.LastLogonDate
                    "Created" = $User.Created
                    "Modified" = $User.Modified
                    "Protected" = $User.ProtectedFromAccidentalDeletion
                    "Domain" = $Domain
                }
            }
            $Groups = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDistingusishedName -ADCatalog $DomainGroupsFullList -DistinguishedName $U}
            foreach ($Group in $Groups) {
                $GroupMembership = Get-ADGroupMember -Server $Domain -Identity $Group.SID -Recursive
                foreach ($Member in $GroupMembership) {
                    $Object = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $Member.DistinguishedName)
                    [pscustomobject][ordered] @{'Policy Name' = $Policy.Name
                        Name = $Group.Name
                        SamAccountName = $Group.SamAccountName
                        Type = $Group.ObjectClass
                        SID = $Group.SID
                        'High Privileged Group' = if ($Group.adminCount -eq 1) {$True} else {$False}
                        'Display Name' = $Object.DisplayName
                        'Member Name' = $Member.Name
                        'User Principal Name' = $Object.UserPrincipalName
                        'Sam Account Name' = $Object.SamAccountName
                        'Email Address' = $Object.EmailAddress
                        'PasswordExpired' = $Object.PasswordExpired
                        'PasswordLastSet' = $Object.PasswordLastSet
                        'PasswordNotRequired' = $Object.PasswordNotRequired
                        'PasswordNeverExpires' = $Object.PasswordNeverExpires
                        'Enabled' = $Object.Enabled
                        'MemberSID' = $Member.SID.Value
                        'Manager' = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $Object.Manager).Name
                        'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $Object.Manager).EmailAddress
                        'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")
                        "DaysToExpire" = (Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                        "AccountExpirationDate" = $Object.AccountExpirationDate
                        "AccountLockoutTime" = $Object.AccountLockoutTime
                        "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                        "BadLogonCount" = $Object.BadLogonCount
                        "CannotChangePassword" = $Object.CannotChangePassword
                        "CanonicalName" = $Object.CanonicalName
                        'Given Name' = $Object.GivenName
                        'Surname' = $Object.Surname
                        "Description" = $Object.Description
                        "DistinguishedName" = $Object.DistinguishedName
                        "EmployeeID" = $Object.EmployeeID
                        "EmployeeNumber" = $Object.EmployeeNumber
                        "LastBadPasswordAttempt" = $Object.LastBadPasswordAttempt
                        "LastLogonDate" = $Object.LastLogonDate
                        "Created" = $Object.Created
                        "Modified" = $Object.Modified
                        "Protected" = $Object.ProtectedFromAccidentalDeletion
                        "Domain" = $Domain
                    }
                }
            }
        })
    return $PolicyUsers
}
function Get-WinADDomainFSMO {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation)
    [ordered] @{'PDC Emulator' = $DomainInformation.PDCEmulator
        'RID Master' = $DomainInformation.RIDMaster
        'Infrastructure Master' = $DomainInformation.InfrastructureMaster
    }
}
function Get-WinADDomainGroupPolicies {
    [CmdletBinding()]
    param([Array] $GroupPolicies,
        [string] $Domain = $Env:USERDNSDOMAIN)
    if ($null -eq $GroupPolicies) {$GroupPolicies = Get-GPO -Domain $Domain -All}
    foreach ($gpo in $GroupPolicies) {
        [PSCustomObject][ordered] @{'Display Name' = $gpo.DisplayName
            'Gpo Status' = $gpo.GPOStatus
            'Creation Time' = $gpo.CreationTime
            'Modification Time' = $gpo.ModificationTime
            'Description' = $gpo.Description
            'Wmi Filter' = $gpo.WmiFilter
        }
    }
}
function Get-WinADDomainGroupPoliciesACL {
    [CmdletBinding()]
    param([Array] $GroupPolicies,
        [string] $Domain = $Env:USERDNSDOMAIN)
    if ($null -eq $GroupPolicies) {$GroupPolicies = Get-GPO -Domain $Domain -All}
    $Output = ForEach ($GPO in $GroupPolicies) {
        [xml]$XmlGPReport = $GPO.generatereport('xml')
        $ACLs = $XmlGPReport.GPO.SecurityDescriptor.Permissions.TrusteePermissions
        foreach ($ACL in $ACLS) {
            [PSCustomObject][ordered] @{'GPO Name' = $GPO.DisplayName
                'User' = $ACL.trustee.name.'#Text'
                'Permission Type' = $ACL.type.PermissionType
                'Inherited' = $ACL.Inherited
                'Permissions' = $ACL.Standard.GPOGroupedAccessEnum
            }
        }
    }
    return $Output
}
function Get-WinADDomainGroupPoliciesDetails {
    [CmdletBinding()]
    param([Array] $GroupPolicies,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [string] $Splitter)
    if ($null -eq $GroupPolicies) {$GroupPolicies = Get-GPO -Domain $Domain -All}
    ForEach ($GPO in $GroupPolicies) {
        [xml]$XmlGPReport = $GPO.generatereport('xml')
        if ($XmlGPReport.GPO.Computer.VersionDirectory -eq 0 -and $XmlGPReport.GPO.Computer.VersionSysvol -eq 0) {$ComputerSettings = "NeverModified"} else {$ComputerSettings = "Modified"}
        if ($XmlGPReport.GPO.User.VersionDirectory -eq 0 -and $XmlGPReport.GPO.User.VersionSysvol -eq 0) {$UserSettings = "NeverModified"} else {$UserSettings = "Modified"}
        if ($null -eq $XmlGPReport.GPO.User.ExtensionData) {$UserSettingsConfigured = $false} else {$UserSettingsConfigured = $true}
        if ($null -eq $XmlGPReport.GPO.Computer.ExtensionData) {$ComputerSettingsConfigured = $false} else {$ComputerSettingsConfigured = $true}
        [PSCustomObject][ordered] @{'Name' = $XmlGPReport.GPO.Name
            'Links' = $XmlGPReport.GPO.LinksTo | Select-Object -ExpandProperty SOMPath
            'Has Computer Settings' = $ComputerSettingsConfigured
            'Has User Settings' = $UserSettingsConfigured
            'User Enabled' = $XmlGPReport.GPO.User.Enabled
            'Computer Enabled' = $XmlGPReport.GPO.Computer.Enabled
            'Computer Settings' = $ComputerSettings
            'User Settings' = $UserSettings
            'Gpo Status' = $GPO.GpoStatus
            'Creation Time' = $GPO.CreationTime
            'Modification Time' = $GPO.ModificationTime
            'WMI Filter' = $GPO.WmiFilter.name
            'WMI Filter Description' = $GPO.WmiFilter.Description
            'Path' = $GPO.Path
            'GUID' = $GPO.Id
            'SDDL' = if ($Splitter -ne '') {$XmlGPReport.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter} else {$XmlGPReport.GPO.SecurityDescriptor.SDDL.'#text'}
        }
    }
}
function Get-WinADDomainGroupsFullList {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [HashTable] $DomainObjects,
        [int] $ResultPageSize = 500000)
    if ($Extended) {[string] $Properties = '*'} else {
        [string[]] $Properties = @('adminCount'
            'CanonicalName'
            'CN'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'GroupCategory'
            'GroupScope'
            'groupType'
            'HomePage'
            'instanceType'
            'isCriticalSystemObject'
            'isDeleted'
            'LastKnownParent'
            'ManagedBy'
            'member'
            'MemberOf'
            'Members'
            'Modified'
            'modifyTimeStamp'
            'Name'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'ProtectedFromAccidentalDeletion'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'SID'
            'SIDHistory'
            'systemFlags'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated')
    }
    $Groups = Get-ADGroup -Server $Domain -Filter * -ResultPageSize $ResultPageSize -Properties $Properties
    foreach ($_ in $Groups) {$DomainObjects.$($_.DistinguishedName) = $_}
    $Groups
}
function Get-DomainGroupsPriviliged {
    [cmdletbinding()]
    param([Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation,
        [Array] $DomainGroups)
    $PrivilegedGroupsSID = "S-1-5-32-544", "S-1-5-32-548", "S-1-5-32-549", "S-1-5-32-550", "S-1-5-32-551", "S-1-5-32-552", "S-1-5-32-556", "S-1-5-32-557", "S-1-5-32-573", "S-1-5-32-578", "S-1-5-32-580", "$($DomainInformation.DomainSID)-512", "$($DomainInformation.DomainSID)-518", "$($DomainInformation.DomainSID)D-519", "$($DomainInformation.DomainSID)-520"
    foreach ($_ in $DomainGroups) {if ($PrivilegedGroupsSID -contains $_.'Group SID') {$_}}
}
function Get-WinADDomainGroupsPriviligedMembers {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembers,
        [Array] $DomainGroupsPriviliged)
    $DomainGroupsMembers | Where-Object {$DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID')} | Select-Object *
}
function Get-WinADDomainGroupsPriviligedMembersRecursive {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembersRecursive,
        [Array] $DomainGroupsPriviliged)
    $DomainGroupsMembersRecursive | Where-Object {$DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID')} | Select-Object *
}
function Get-WinADDomainGroupsSpecial {
    [CmdletBinding()]
    param([Array] $DomainGroups)
    $DomainGroups | Where-Object {($_.'Group SID').Length -eq 12}
}
function Get-WinADDomainGroupsSpecialMembers {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembers)
    $DomainGroupsMembers | Where-Object {($_.'Group SID').Length -eq 12} | Select-Object *
}
function Get-WinADDomainGroupsSpecialMembersRecursive {
    [CmdletBinding()]
    param([Array] $DomainGroupsMembersRecursive)
    $DomainGroupsMembersRecursive | Where-Object {($_.'Group SID').Length -eq 12} | Select-Object *
}
function Get-WinADDomainGUIDs {
    [cmdletbinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Microsoft.ActiveDirectory.Management.ADEntity] $RootDSE)
    if ($null -eq $RootDSE) {$RootDSE = Get-ADRootDSE -Server $Domain}
    $GUID = @{}
    $Schema = Get-ADObject -SearchBase $RootDSE.schemaNamingContext -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID
    foreach ($S in $Schema) {if ($GUID.Keys -notcontains $S.schemaIDGUID) {$GUID.add([System.GUID]$S.schemaIDGUID, $S.name)}}
    $Extended = Get-ADObject -SearchBase "CN=Extended-Rights,$($RootDSE.configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID
    foreach ($S in $Extended) {if ($GUID.Keys -notcontains $S.rightsGUID) {$GUID.add([System.GUID]$S.rightsGUID, $S.name)}}
    return $GUID
}
function Get-WinADDomainInformation {
    [CmdletBinding()]
    param ([string] $Domain,
        [PSWinDocumentation.ActiveDirectory[]] $TypesRequired,
        [string] $PathToPasswords,
        [string] $PathToPasswordsHashes,
        [switch] $Extended,
        [Array] $ForestSchemaComputers,
        [Array] $ForestSchemaUsers,
        [switch] $PasswordQuality,
        [alias('Joiner')][string] $Splitter,
        [switch] $Parallel,
        [int] $ResultPageSize = 500000)
    $Data = [ordered] @{}
    if ($Domain -eq '') {
        Write-Warning 'Get-WinADDomainInformation - $Domain parameter is empty. Try your domain name like ad.evotec.xyz. Skipping for now...'
        return
    }
    if ($null -eq $TypesRequired) {
        Write-Verbose 'Get-WinADDomainInformation - TypesRequired is null. Getting all.'
        $TypesRequired = Get-Types -Types ([PSWinDocumentation.ActiveDirectory])
    }
    $TimeToGenerate = Start-TimeLog
    if ($null -eq $ForestSchemaComputers) {
        $ForestSchemaComputers = Get-DataInformation -Text "Getting domain information - ForestSchemaPropertiesComputers" {Get-WinADForestSchemaPropertiesComputers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSchemaPropertiesComputers
            [PSWinDocumentation.ActiveDirectory]::DomainComputersFullList
            [PSWinDocumentation.ActiveDirectory]::DomainComputersAll
            [PSWinDocumentation.ActiveDirectory]::DomainComputersAllCount
            [PSWinDocumentation.ActiveDirectory]::DomainServers
            [PSWinDocumentation.ActiveDirectory]::DomainServersCount
            [PSWinDocumentation.ActiveDirectory]::DomainComputers
            [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
            [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
            [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
            [PSWinDocumentation.ActiveDirectory]::DomainBitlocker
            [PSWinDocumentation.ActiveDirectory]::DomainLAPS)
    }
    if ($null -eq $ForestSchemaUsers) {
        $ForestSchemaUsers = Get-DataInformation -Text "Getting domain information - ForestSchemaPropertiesUsers" {Get-WinADForestSchemaPropertiesUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSchemaPropertiesUsers
            [PSWinDocumentation.ActiveDirectory]::DomainUsersFullList)
    }
    $Data.DomainObjects = @{}
    $Data.DomainRootDSE = Get-DataInformation -Text "Getting domain information - $Domain DomainRootDSE" {Get-WinADRootDSE -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainRootDSE
        [PSWinDocumentation.ActiveDirectory]::DomainGUIDS
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtendedACL)
    $Data.DomainInformation = Get-DataInformation -Text "Getting domain information - $Domain DomainInformation" {Get-WinADDomain -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainInformation
        [PSWinDocumentation.ActiveDirectory]::DomainRIDs
        [PSWinDocumentation.ActiveDirectory]::DomainFSMO
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsDN
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtendedACL
        [PSWinDocumentation.ActiveDirectory]::DomainAdministrators
        [PSWinDocumentation.ActiveDirectory]::DomainAdministratorsRecursive
        [PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministrators
        [PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministratorsRecursive
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordStats,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)
    $Data.DomainGroupsFullList = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsFullList" {Get-WinADDomainGroupsFullList -Domain $Domain -DomainObjects $Data.DomainObjects -ResultPageSize $ResultPageSize} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsFullList
        [PSWinDocumentation.ActiveDirectory]::DomainUsers
        [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsers
        [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended
        [PSWinDocumentation.ActiveDirectory]::DomainGroups
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembersRecursive)
    $Data.DomainUsersFullList = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersFullList" {Get-WinADDomainUsersFullList -Domain $Domain -Extended:$Extended -ForestSchemaUsers $ForestSchemaUsers -DomainObjects $Data.DomainObjects -ResultPageSize $ResultPageSize} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersFullList
        [PSWinDocumentation.ActiveDirectory]::DomainUsers
        [PSWinDocumentation.ActiveDirectory]::DomainUsersAll
        [PSWinDocumentation.ActiveDirectory]::DomainUsersSystemAccounts
        [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiring
        [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiringInclDisabled
        [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredInclDisabled
        [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredExclDisabled
        [PSWinDocumentation.ActiveDirectory]::DomainUsersCount
        [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsers
        [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended
        [PSWinDocumentation.ActiveDirectory]::DomainGroups
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembersRecursive)
    $Data.DomainComputersFullList = Get-DataInformation -Text "Getting domain information - $Domain DomainComputersFullList" {Get-WinADDomainComputersFullList -Domain $Domain -ForestSchemaComputers $ForestSchemaComputers -DomainObjects $Data.DomainObjects -ResultPageSize $ResultPageSize} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputersFullList
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAll
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAllCount
        [PSWinDocumentation.ActiveDirectory]::DomainServers
        [PSWinDocumentation.ActiveDirectory]::DomainServersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputers
        [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
        [PSWinDocumentation.ActiveDirectory]::DomainBitlocker
        [PSWinDocumentation.ActiveDirectory]::DomainLAPS
        [PSWinDocumentation.ActiveDirectory]::DomainUsers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsMembersRecursive)
    $Data.DomainComputersAll = Get-DataInformation -Text "Getting domain information - $Domain DomainComputersAll" {Get-WinADDomainComputersAll -DomainComputersFullList $Data.DomainComputersFullList -Splitter $Splitter -DomainObjects $Data.DomainObjects} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputersAll
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAllCount
        [PSWinDocumentation.ActiveDirectory]::DomainServers
        [PSWinDocumentation.ActiveDirectory]::DomainServersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputers
        [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords)
    $Data.DomainComputersAllCount = Get-DataInformation -Text "Getting domain information - $Domain DomainComputersAllCount" {Get-WinADDomainComputersAllCount -DomainComputersAll $Data.DomainComputersAll} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputersAllCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAll)
    $Data.DomainServers = Get-DataInformation -Text "Getting domain information - $Domain DomainServers" {Get-WinADDomainServers -DomainComputersAll $Data.DomainComputersAll} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainServers
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAll)
    $Data.DomainServersCount = Get-DataInformation -Text "Getting domain information - $Domain DomainServersCount" {Get-WinADDomainServersCount -DomainServers $Data.DomainServers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainServersCount
        [PSWinDocumentation.ActiveDirectory]::DomainServers)
    $Data.DomainComputers = Get-DataInformation -Text "Getting domain information - $Domain DomainComputers" {Get-WinADDomainComputers -DomainComputersAll $Data.DomainComputersAll} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputers
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAll)
    $Data.DomainComputersCount = Get-DataInformation -Text "Getting domain information - $Domain DomainComputersCount" {Get-WinADDomainComputersCount -DomainComputers $Data.DomainComputers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputers)
    $Data.DomainComputersUnknown = Get-DataInformation -Text "Getting domain information - $Domain DomainComputersUnknown" {Get-WinADDomainComputersUnknown -DomainComputersAll $Data.DomainComputersAll} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAll)
    $Data.DomainComputersUnknownCount = Get-DataInformation -Text "Getting domain information - $Domain DomainComputersUnknownCount" {Get-WinADDomainComputersUnknownCount -DomainComputersUnknown $Data.DomainComputersUnknown} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown)
    $Data.DomainRIDs = Get-DataInformation -Text "Getting domain information - $Domain DomainRIDs" {Get-WinADDomainRIDs -DomainInformation $Data.DomainInformation -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainRIDs)
    $Data.DomainGUIDS = Get-DataInformation -Text "Getting domain information - $Domain DomainGUIDS" {Get-WinADDomainGUIDs -RootDSE $Data.DomainRootDSE -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGUIDS
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtendedACL)
    $Data.DomainAuthenticationPolicies = Get-DataInformation -Text "Getting domain information - $Domain DomainAuthenticationPolicies" {Get-ADAuthenticationPolicy -Server $Domain -LDAPFilter '(name=AuthenticationPolicy*)'} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainAuthenticationPolicies)
    $Data.DomainAuthenticationPolicySilos = Get-DataInformation -Text "Getting domain information - $Domain DomainAuthenticationPolicySilos" {Get-ADAuthenticationPolicySilo -Server $Domain -Filter 'Name -like "*AuthenticationPolicySilo*"'} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainAuthenticationPolicySilos)
    $Data.DomainCentralAccessPolicies = Get-DataInformation -Text "Getting domain information - $Domain DomainCentralAccessPolicies" {Get-ADCentralAccessPolicy -Server $Domain -Filter *} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainCentralAccessPolicies)
    $Data.DomainCentralAccessRules = Get-DataInformation -Text "Getting domain information - $Domain DomainCentralAccessRules" {Get-ADCentralAccessRule -Server $Domain -Filter *} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainCentralAccessRules)
    $Data.DomainClaimTransformPolicies = Get-DataInformation -Text "Getting domain information - $Domain DomainClaimTransformPolicies" {Get-ADClaimTransformPolicy -Server $Domain -Filter *} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainClaimTransformPolicies)
    $Data.DomainClaimTypes = Get-DataInformation -Text "Getting domain information - $Domain DomainClaimTypes" {Get-ADClaimType -Server $Domain -Filter *} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainClaimTypes)
    $DomainDNSData = Get-DataInformation -Text "Getting domain information - $Domain DomainDNSData" {Get-WinADDomainDNSData -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainDNSSRV
        [PSWinDocumentation.ActiveDirectory]::DomainDNSA)
    $Data.DomainDNSSrv = Get-DataInformation -Text "Getting domain information - $Domain DomainDNSSrv" {$DomainDNSData.SRV} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainDNSSrv)
    $Data.DomainDNSA = Get-DataInformation -Text "Getting domain information - $Domain DomainDNSA" {$DomainDNSData.A} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainDNSA)
    $Data.DomainFSMO = Get-DataInformation -Text "Getting domain information - $Domain DomainFSMO" {Get-WinADDomainFSMO -Domain $Domain -DomainInformation $Data.DomainInformation} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFSMO
        [PSWinDocumentation.ActiveDirectory]::DomainTrusts)
    $Data.DomainTrustsClean = Get-DataInformation -Text "Getting domain information - $Domain DomainTrustsClean" {Get-WinADDomainTrustsClean -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainTrustsClean
        [PSWinDocumentation.ActiveDirectory]::DomainTrusts)
    $Data.DomainTrusts = Get-DataInformation -Text "Getting domain information - $Domain DomainTrusts" {Get-WinADDomainTrusts -DomainPDC $Data.DomainFSMO.'PDC Emulator' -Trusts $Data.DomainTrustsClean -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainTrusts)
    $Data.DomainGroupPoliciesClean = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupPoliciesClean" {Get-GPO -Domain $Domain -All} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesClean
        [PSWinDocumentation.ActiveDirectory]::DomainGroupPolicies
        [PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesDetails
        [PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesACL)
    $Data.DomainGroupPolicies = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupPolicies" {Get-WinADDomainGroupPolicies -GroupPolicies $Data.DomainGroupPoliciesClean -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupPolicies)
    $Data.DomainGroupPoliciesDetails = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupPoliciesDetails" {Get-WinADDomainGroupPoliciesDetails -GroupPolicies $Data.DomainGroupPoliciesClean -Domain $Domain -Splitter $Splitter} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesDetails)
    $Data.DomainGroupPoliciesACL = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupPoliciesACL" {Get-WinADDomainGroupPoliciesACL -GroupPolicies $Data.DomainGroupPoliciesClean -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesACL)
    $Data.DomainBitlocker = Get-DataInformation -Text "Getting domain information - $Domain DomainBitlocker" {Get-WinADDomainBitlocker -Domain $Domain -Computers $Data.DomainComputersFullList} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainBitlocker)
    $Data.DomainLAPS = Get-DataInformation -Text "Getting domain information - $Domain DomainLAPS" {Get-WinADDomainLAPS -Domain $Domain -Computers $Data.DomainComputersFullList -Splitter $Splitter} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainLAPS)
    $Data.DomainDefaultPasswordPolicy = Get-DataInformation -Text "Getting domain information - $Domain DomainDefaultPasswordPolicy" {Get-WinADDomainDefaultPasswordPolicy -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainDefaultPasswordPolicy)
    $Data.DomainOrganizationalUnitsClean = Get-DataInformation -Text "Getting domain information - $Domain DomainOrganizationalUnitsClean" {Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter *} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsClean
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnits
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsDN
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL
        [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtendedACL)
    $Data.DomainOrganizationalUnits = Get-DataInformation -Text "Getting domain information - $Domain DomainOrganizationalUnits" {Get-WinADDomainOrganizationalUnits -Domain $Domain -OrgnaizationalUnits $Data.DomainOrganizationalUnitsClean -DomainObjects $Data.DomainObjects} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnits)
    $Data.DomainOrganizationalUnitsDN = Get-DataInformation -Text "Getting domain information - $Domain DomainOrganizationalUnitsDN" {@($Data.DomainInformation.DistinguishedName
            $Data.DomainOrganizationalUnitsClean.DistinguishedName
            $Data.DomainContainers.DistinguishedName)} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsDN)
    $Data.DomainOrganizationalUnitsBasicACL = Get-DataInformation -Text "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL" {Get-WinADDomainOrganizationalUnitsACL -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean -Domain $Domain -NetBiosName $Data.DomainInformation.NetBIOSName -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL)
    $Data.DomainOrganizationalUnitsExtendedACL = Get-DataInformation -Text "Getting domain information - $Domain DomainOrganizationalUnitsExtendedACL" {Get-WinADDomainOrganizationalUnitsACLExtended -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean -Domain $Domain -NetBiosName $Data.DomainInformation.NetBIOSName -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext -GUID $Data.DomainGUIDS} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtendedACL)
    $Data.DomainUsers = Get-DataInformation -Text "Getting domain information - $Domain DomainUsers" {Get-WinADDomainUsersAll -Users $Data.DomainUsersFullList -Domain $Domain -DomainObjects $Data.DomainObjects -Splitter $Splitter} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsers
        [PSWinDocumentation.ActiveDirectory]::DomainUsersAll
        [PSWinDocumentation.ActiveDirectory]::DomainUsersSystemAccounts
        [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiring
        [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiringInclDisabled
        [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredInclDisabled
        [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredExclDisabled
        [PSWinDocumentation.ActiveDirectory]::DomainUsersCount
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords)
    $Data.DomainUsersAll = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersAll" {Get-WinADDomainUsersAllFiltered -DomainUsers $Data.DomainUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersAll)
    $Data.DomainUsersSystemAccounts = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersSystemAccounts" {Get-WinADDomainUsersSystemAccounts -DomainUsers $Data.DomainUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersSystemAccounts)
    $Data.DomainUsersNeverExpiring = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersNeverExpiring" {Get-WinADDomainUsersNeverExpiring -DomainUsers $Data.DomainUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiring)
    $Data.DomainUsersNeverExpiringInclDisabled = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersNeverExpiringInclDisabled" {Get-WinADDomainUsersNeverExpiringInclDisabled -DomainUsers $Data.DomainUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiringInclDisabled)
    $Data.DomainUsersExpiredInclDisabled = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersExpiredInclDisabled" {Get-WinADDomainUsersExpiredInclDisabled -DomainUsers $Data.DomainUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredInclDisabled)
    $Data.DomainUsersExpiredExclDisabled = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersExpiredExclDisabled" {Get-WinADDomainUsersExpiredExclDisabled -DomainUsers $Data.DomainUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredExclDisabled)
    $Data.DomainUsersCount = Get-DataInformation -Text "Getting domain information - $Domain DomainUsersCount" {Get-WinADDomainAllUsersCount -DomainUsers $Data.DomainUsers -DomainUsersAll $Data.DomainUsersAll -DomainUsersExpiredExclDisabled $Data.DomainUsersExpiredExclDisabled -DomainUsersExpiredInclDisabled $Data.DomainUsersExpiredInclDisabled -DomainUsersNeverExpiring $Data.DomainUsersNeverExpiring -DomainUsersNeverExpiringInclDisabled $Data.DomainUsersNeverExpiringInclDisabled -DomainUsersSystemAccounts $Data.DomainUsersSystemAccounts} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsersCount)
    $Data.DomainControllers = Get-DataInformation -Text "Getting domain information - $Domain DomainControllers" {Get-WinADDomainControllersInternal -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainControllers)
    $Data.DomainFineGrainedPolicies = Get-DataInformation -Text "Getting domain information - $Domain DomainFineGrainedPolicies" {Get-WinADDomainFineGrainedPolicies -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPolicies
        [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsers
        [PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended)
    $Data.DomainFineGrainedPoliciesUsers = Get-DataInformation -Text "Getting domain information - $Domain DomainFineGrainedPoliciesUsers" {Get-WinADDomainFineGrainedPoliciesUsers -DomainFineGrainedPolicies $Data.DomainFineGrainedPolicies -DomainObjects $Data.DomainObjects} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsers)
    $Data.DomainFineGrainedPoliciesUsersExtended = Get-DataInformation -Text "Getting domain information - $Domain DomainFineGrainedPoliciesUsersExtended" {Get-WinADDomainFineGrainedPoliciesUsersExtended -DomainFineGrainedPolicies $Data.DomainFineGrainedPoliciesUsers -DomainUsersFullList $Data.DomainUsersFullList -DomainGroupsFullList $Data.DomainGroupsFullList -Domain $Domain} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended)
    $Data.DomainGroups = Get-DataInformation -Text "Getting domain information - $Domain DomainGroups" {Get-WinGroups -Groups $Data.DomainGroupsFullList -Domain $Domain -Splitter $Splitter -DomainObjects $Data.DomainObjects} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroups
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecial)
    $Data.DomainGroupsMembers = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsMembers" {Get-WinGroupMembers -Groups $Data.DomainGroups -Domain $Domain -Option Standard -DomainObjects $Data.DomainObjects} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsMembers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembers
        [PSWinDocumentation.ActiveDirectory]::DomainAdministrators
        [PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministrators)
    $Data.DomainGroupsMembersRecursive = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsMembersRecursive" {Get-WinGroupMembers -Groups $Data.DomainGroups -Domain $Domain -Option Recursive -DomainObjects $Data.DomainObjects} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsMembersRecursive
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembersRecursive
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembersRecursive
        [PSWinDocumentation.ActiveDirectory]::DomainAdministratorsRecursive
        [PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministratorsRecursive)
    $Data.DomainGroupsPriviliged = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsPriviliged" {Get-DomainGroupsPriviliged -DomainGroups $Data.DomainGroups -DomainInformation $Data.DomainInformation} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembers
        [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembersRecursive)
    $Data.DomainGroupsSpecial = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsSpecial" {Get-WinADDomainGroupsSpecial -DomainGroups $Data.DomainGroups} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecial
        [PSWinDocumentation.ActiveDirectory]::DomainGroupMembersRecursiveSpecial)
    $Data.DomainGroupsSpecialMembers = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsSpecialMembers" {Get-WinADDomainGroupsSpecialMembers -DomainGroupsMembers $Data.DomainGroupsMembers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembers)
    $Data.DomainGroupsSpecialMembersRecursive = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsSpecialMembersRecursive" {Get-WinADDomainGroupsSpecialMembersRecursive -DomainGroupsMembersRecursive $Data.DomainGroupsMembersRecursive} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembersRecursive)
    $Data.DomainGroupsPriviligedMembers = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsPriviligedMembers" {Get-WinADDomainGroupsPriviligedMembers -DomainGroupsMembers $Data.DomainGroupsMembers -DomainGroupsPriviliged $Data.DomainGroupsPriviliged} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembers)
    $Data.DomainGroupsPriviligedMembersRecursive = Get-DataInformation -Text "Getting domain information - $Domain DomainGroupsPriviligedMembersRecursive" {Get-WinADDomainGroupsPriviligedMembersRecursive -DomainGroupsMembersRecursive $Data.DomainGroupsMembersRecursive -DomainGroupsPriviliged $Data.DomainGroupsPriviliged} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembersRecursive)
    $Data.DomainAdministrators = Get-DataInformation -Text "Getting domain information - $Domain DomainAdministrators" {Get-WinADDomainAdministrators -DomainGroupsMembers $Data.DomainGroupsMembers -DomainInformation $Data.DomainInformation} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainAdministrators)
    $Data.DomainAdministratorsRecursive = Get-DataInformation -Text "Getting domain information - $Domain DomainAdministratorsRecursive" {Get-WinADDomainAdministratorsRecursive -DomainGroupsMembersRecursive $Data.DomainGroupsMembersRecursive -DomainInformation $Data.DomainInformation} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainAdministratorsRecursive)
    $Data.DomainEnterpriseAdministrators = Get-DataInformation -Text "Getting domain information - $Domain DomainEnterpriseAdministrators" {Get-WinADDomainEnterpriseAdministrators -DomainGroupsMembers $Data.DomainGroupsMembers -DomainInformation $Data.DomainInformation} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministrators)
    $Data.DomainEnterpriseAdministratorsRecursive = Get-DataInformation -Text "Getting domain information - $Domain DomainEnterpriseAdministratorsRecursive" {Get-WinADDomainEnterpriseAdministratorsRecursive -DomainGroupsMembersRecursive $Data.DomainGroupsMembersRecursive -DomainInformation $Data.DomainInformation} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministratorsRecursive)
    $Data.DomainPasswordDataUsers = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDataUsers" {Get-WinADDomainPassword -DnsRoot $Data.DomainInformation.DNSRoot -DistinguishedName $Data.DomainInformation.DistinguishedName} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDataUsers,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordStats,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)
    $Data.DomainPasswordDataPasswords = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDataPasswords" {Get-WinADDomainPasswordQuality -FilePath $PathToPasswords -DomainComputersAll $Data.DomainComputersAll -DomainUsersAll $Data.DomainUsersAll -DomainDistinguishedName $Data.DomainInformation.DistinguishedName -DnsRoot $Data.DomainInformation.DnsRoot -Verbose:$false -PasswordQualityUsers $Data.DomainPasswordDataUsers -PasswordQuality:$PasswordQuality} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordStats,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)
    $Data.DomainPasswordDataPasswordsHashes = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDataPasswordsHashes" {Get-WinADDomainPasswordQuality -FilePath $PathToPasswordsHashes -DomainComputersAll $Data.DomainComputersAll -DomainUsersAll $Data.DomainUsersAll -DomainDistinguishedName $Data.DomainInformation.DistinguishedName -DnsRoot $DomainInformation.DnsRoot -UseHashes -Verbose:$false -PasswordQualityUsers $Data.DomainPasswordDataUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
        [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)
    if ($Data.DomainPasswordDataPasswords) {$PasswordsQuality = $Data.DomainPasswordDataPasswords} elseif ($Data.DomainPasswordDataPasswordsHashes) {$PasswordsQuality = $Data.DomainPasswordDataPasswordsHashes} else {$PasswordsQuality = $null}
    $Data.DomainPasswordClearTextPassword = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordClearTextPassword" {$PasswordsQuality.DomainPasswordClearTextPassword} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword)
    $Data.DomainPasswordLMHash = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordLMHash" {$PasswordsQuality.DomainPasswordLMHash} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash)
    $Data.DomainPasswordEmptyPassword = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordEmptyPassword" {$PasswordsQuality.DomainPasswordEmptyPassword} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword)
    $Data.DomainPasswordEmptyPassword = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordEmptyPassword" {$PasswordsQuality.DomainPasswordEmptyPassword} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword)
    $Data.DomainPasswordWeakPassword = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordWeakPassword" {$PasswordsQuality.DomainPasswordWeakPassword} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword)
    $Data.DomainPasswordWeakPasswordEnabled = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordWeakPasswordEnabled" {$PasswordsQuality.DomainPasswordWeakPasswordEnabled} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled)
    $Data.DomainPasswordWeakPasswordDisabled = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordWeakPasswordDisabled" {$PasswordsQuality.DomainPasswordWeakPasswordDisabled} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled)
    $Data.DomainPasswordWeakPasswordList = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordWeakPasswordList" {$PasswordsQuality.DomainPasswordWeakPasswordList} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList)
    $Data.DomainPasswordDefaultComputerPassword = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDefaultComputerPassword" {$PasswordsQuality.DomainPasswordDefaultComputerPassword} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword)
    $Data.DomainPasswordPasswordNotRequired = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordPasswordNotRequired" {$PasswordsQuality.DomainPasswordPasswordNotRequired} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired)
    $Data.DomainPasswordPasswordNeverExpires = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordPasswordNeverExpires" {$PasswordsQuality.DomainPasswordPasswordNeverExpires} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires)
    $Data.DomainPasswordAESKeysMissing = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordAESKeysMissing" {$PasswordsQuality.DomainPasswordAESKeysMissing} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing)
    $Data.DomainPasswordPreAuthNotRequired = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordPreAuthNotRequired" {$PasswordsQuality.DomainPasswordPreAuthNotRequired} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired)
    $Data.DomainPasswordDESEncryptionOnly = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDESEncryptionOnly" {$PasswordsQuality.DomainPasswordDESEncryptionOnly} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly)
    $Data.DomainPasswordDelegatableAdmins = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDelegatableAdmins" {$PasswordsQuality.DomainPasswordDelegatableAdmins} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins)
    $Data.DomainPasswordDuplicatePasswordGroups = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordDuplicatePasswordGroups" {$PasswordsQuality.DomainPasswordDuplicatePasswordGroups} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups)
    $Data.DomainPasswordHashesWeakPassword = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordHashesWeakPassword" {$PasswordsQuality.DomainPasswordHashesWeakPassword} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword)
    $Data.DomainPasswordHashesWeakPasswordEnabled = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordHashesWeakPasswordEnabled" {$PasswordsQuality.DomainPasswordHashesWeakPasswordEnabled} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)
    $Data.DomainPasswordHashesWeakPasswordDisabled = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordHashesWeakPasswordDisabled" {$Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordDisabled} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)
    $Data.DomainPasswordStats = Get-DataInformation -Text "Getting domain information - $Domain DomainPasswordStats" {Get-WinADDomainPasswordStats -PasswordsQuality $PasswordsQuality -TypesRequired $TypesRequired -DomainPasswordHashesWeakPassword $Data.DomainPasswordHashesWeakPassword -DomainPasswordHashesWeakPasswordEnabled $Data.DomainPasswordHashesWeakPasswordEnabled -DomainPasswordHashesWeakPasswordDisabled $Data.DomainPasswordHashesWeakPasswordDisabled} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordStats)
    $EndTime = Stop-TimeLog -Time $TimeToGenerate
    Write-Verbose "Getting domain information - $Domain - Time to generate: $EndTime"
    return $Data
}
function Get-WinADDomainLAPS {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $Computers,
        [string] $Splitter)
    $Properties = @('Name',
        'OperatingSystem',
        'DistinguishedName',
        'ms-Mcs-AdmPwd',
        'ms-Mcs-AdmPwdExpirationTime')
    [DateTime] $CurrentDate = Get-Date
    if ($null -eq $Computers -or $Computers.Count -eq 0) {$Computers = Get-ADComputer -Filter * -Properties $Properties}
    foreach ($Computer in $Computers) {
        [PSCustomObject] @{'Name' = $Computer.Name
            'Operating System' = $Computer.'OperatingSystem'
            'LapsPassword' = if ($Splitter -ne '') {$Computer.'ms-Mcs-AdmPwd' -join $Splitter} else {$Computer.'ms-Mcs-AdmPwd'}
            'LapsExpire(days)' = Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime'))
            'LapsExpirationTime' = Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime')
            'DistinguishedName' = $Computer.'DistinguishedName'
        }
    }
}
function Get-WinADDomainOrganizationalUnits {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $OrgnaizationalUnits,
        [hashtable] $DomainObjects)
    if ($null -eq $OrgnaizationalUnits) {$OrgnaizationalUnits = $(Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter *)}
    $Output = foreach ($_ in $OrgnaizationalUnits) {
        $Manager = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $_.ManagedBy
        [PSCustomObject] @{'Canonical Name' = $_.CanonicalName
            'Managed By' = $Manager.Name
            'Manager Email' = $Manager.EmailAddress
            'Protected' = $_.ProtectedFromAccidentalDeletion
            Description = $_.Description
            Created = $_.Created
            Modified = $_.Modified
            Deleted = $_.Deleted
            'PostalCode' = $_.PostalCode
            City = $_.City
            Country = $_.Country
            State = $_.State
            'StreetAddress' = $_.StreetAddress
            DistinguishedName = $_.DistinguishedName
            ObjectGUID = $_.ObjectGUID
        }
    }
    $Output | Sort-Object 'Canonical Name'
}
function Get-WinADDomainOrganizationalUnitsACL {
    [cmdletbinding()]
    param($DomainOrganizationalUnitsClean,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [string] $NetBiosName,
        [string] $RootDomainNamingContext)
    $OUs = @(foreach ($OU in $DomainOrganizationalUnitsClean) {@{Name = 'Organizational Unit'; Value = $OU.DistinguishedName}
        })
    $null = New-PSDrive -Name $NetBiosName -Root '' -PsProvider ActiveDirectory -Server $Domain
    @(foreach ($OU in $OUs) {
            $ACL = Get-Acl -Path "$NetBiosName`:\$($OU.Value)"
            [PsCustomObject] @{'Distinguished Name' = $OU.Value
                'Type' = $OU.Name
                'Owner' = $ACL.Owner
                'Group' = $ACL.Group
                'Are AccessRules Protected' = $ACL.AreAccessRulesProtected
                'Are AuditRules Protected' = $ACL.AreAuditRulesProtected
                'Are AccessRules Canonical' = $ACL.AreAccessRulesCanonical
                'Are AuditRules Canonical' = $ACL.AreAuditRulesCanonical
            }
        })
}
function Get-WinADDomainOrganizationalUnitsACLExtended {
    [cmdletbinding()]
    param($DomainOrganizationalUnitsClean,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [string] $NetBiosName,
        [string] $RootDomainNamingContext,
        $GUID)
    $OUs = @(foreach ($OU in $DomainOrganizationalUnitsClean) {@{Name = 'Organizational Unit'; Value = $OU.DistinguishedName}
        })
    $null = New-PSDrive -Name $NetBiosName -Root '' -PsProvider ActiveDirectory -Server $Domain
    @(foreach ($OU in $OUs) {
            $ACLs = Get-Acl -Path "$NetBiosName`:\$($OU.Value)" | Select-Object -ExpandProperty Access
            foreach ($ACL in $ACLs) {
                [PSCustomObject] @{'Distinguished Name' = $OU.Value
                    'Type' = $OU.Name
                    'AccessControlType' = $ACL.AccessControlType
                    'ObjectType Name' = if ($ACL.objectType.ToString() -eq '00000000-0000-0000-0000-000000000000') {'All'} Else {$GUID.Item($ACL.objectType)}
                    'Inherited ObjectType Name' = $GUID.Item($ACL.inheritedObjectType)
                    'ActiveDirectoryRights' = $ACL.ActiveDirectoryRights
                    'InheritanceType' = $ACL.InheritanceType
                    'ObjectType' = $ACL.ObjectType
                    'InheritedObjectType' = $ACL.InheritedObjectType
                    'ObjectFlags' = $ACL.ObjectFlags
                    'IdentityReference' = $ACL.IdentityReference
                    'IsInherited' = $ACL.IsInherited
                    'InheritanceFlags' = $ACL.InheritanceFlags
                    'PropagationFlags' = $ACL.PropagationFlags
                }
            }
        })
}
function Get-WinADDomainPassword {
    [CmdletBinding()]
    param($DnsRoot,
        $DistinguishedName)
    try {Get-ADReplAccount -All -Server $DnsRoot -NamingContext $DistinguishedName} catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($ErrorMessage -like '*is not recognized as the name of a cmdlet*') {Write-Warning "Get-ADReplAccount - Please install module DSInternals (Install-Module DSInternals) - Error: $ErrorMessage"} else {Write-Warning "Get-ADReplAccount - Error occured: $ErrorMessage"}
    }
}
function Get-WinADDomainPasswordQuality {
    [CmdletBinding()]
    param ([string] $DnsRoot,
        [Array] $DomainUsersAll,
        [Array] $DomainComputersAll,
        [string] $DomainDistinguishedName,
        [Array] $PasswordQualityUsers,
        [string] $FilePath,
        [switch] $UseHashes,
        [switch] $PasswordQuality)
    if ($FilePath -eq '' -and $PasswordQuality.IsPresent -eq $true) {$FilePath = "$PSScriptRoot\Resources\PasswordList.txt"}
    if ($FilePath -eq '') {
        Write-Verbose "Get-WinADDomainPasswordQuality - File path not given, using hashes set to $UseHashes"
        return $null
    }
    if (-not (Test-Path -LiteralPath $FilePath)) {
        Write-Verbose "Get-WinADDomainPasswordQuality - File path doesn't exists, using hashes set to $UseHashes"
        return $null
    }
    $Data = [ordered] @{}
    if ($PasswordQualityUsers) {$Data.PasswordQualityUsers = $PasswordQualityUsers} else {$Data.PasswordQualityUsers = Get-ADReplAccount -All -Server $DnsRoot -NamingContext $DomainDistinguishedName}
    $Data.PasswordQuality = Invoke-Command -ScriptBlock {if ($UseHashes) {$Results = $Data.PasswordQualityUsers | Test-PasswordQuality -WeakPasswordHashesFile $FilePath -IncludeDisabledAccounts} else {$Results = $Data.PasswordQualityUsers | Test-PasswordQuality -WeakPasswordsFile $FilePath -IncludeDisabledAccounts}
        return $Results}
    $Data.DomainPasswordClearTextPassword = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.ClearTextPassword -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordClearTextPasswordEnabled = Invoke-Command -ScriptBlock {return $Data.DomainPasswordClearTextPassword | Where-Object {$_.Enabled -eq $true}}
    $Data.DomainPasswordClearTextPasswordDisabled = Invoke-Command -ScriptBlock {return $Data.DomainPasswordClearTextPassword | Where-Object {$_.Enabled -eq $false}}
    $Data.DomainPasswordLMHash = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.LMHash -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordEmptyPassword = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.EmptyPassword -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordWeakPassword = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.WeakPassword -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordWeakPasswordEnabled = Invoke-Command -ScriptBlock {return $Data.DomainPasswordWeakPassword | Where-Object {$_.Enabled -eq $true}}
    $Data.DomainPasswordWeakPasswordDisabled = Invoke-Command -ScriptBlock {return $Data.DomainPasswordWeakPassword | Where-Object {$_.Enabled -eq $false}}
    $Data.DomainPasswordWeakPasswordList = Invoke-Command -ScriptBlock {if ($UseHashes) {return ''} else {
            $Passwords = Get-Content -Path $FilePath
            return $Passwords -join ', '
        }}
    $Data.DomainPasswordDefaultComputerPassword = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.DefaultComputerPassword -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordPasswordNotRequired = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.PasswordNotRequired -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordPasswordNeverExpires = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.PasswordNeverExpires -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordAESKeysMissing = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.AESKeysMissing -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordPreAuthNotRequired = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.PreAuthNotRequired -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordDESEncryptionOnly = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.DESEncryptionOnly -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordDelegatableAdmins = Invoke-Command -ScriptBlock {$ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.DelegatableAdmins -ADCatalog $DomainUsersAll, $DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    $Data.DomainPasswordDuplicatePasswordGroups = Invoke-Command -ScriptBlock {$DuplicateGroups = $Data.PasswordQuality.DuplicatePasswordGroups.ToArray()
        $Count = 0
        $Value = foreach ($DuplicateGroup in $DuplicateGroups) {
            $Count++
            $Name = "Duplicate $Count"
            foreach ($User in $DuplicateGroup) {$FoundUser = [pscustomobject] @{'Duplicate Group' = $Name}
                $FullUserInformation = foreach ($_ in $DomainUsersAll) {if ($_.SamAccountName -eq $User) {$_}}
                $FullComputerInformation = foreach ($_ in $DomainComputersAll) {if ($_.SamAccountName -eq $User) {$_}}
                if ($FullUserInformation) {$MergedObject = Merge-Objects -Object1 $FoundUser -Object2 $FullUserInformation}
                if ($FullComputerInformation) {$MergedObject = Merge-Objects -Object1 $MergedObject -Object2 $FullComputerInformation}
                $MergedObject
            }
        }
        return $Value | Select-Object 'Duplicate Group', 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', 'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"}
    return $Data
}
function Get-WinADDomainPasswordStats {
    [CmdletBinding()]
    param($PasswordsQuality,
        $TypesRequired,
        $DomainPasswordHashesWeakPassword,
        $DomainPasswordHashesWeakPasswordEnabled,
        $DomainPasswordHashesWeakPasswordDisabled)
    $Stats = [ordered] @{}
    $Stats.'Clear Text Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordClearTextPassword
    $Stats.'LM Hashes' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordLMHash
    $Stats.'Empty Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordEmptyPassword
    $Stats.'Weak Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordWeakPassword
    $Stats.'Weak Passwords Enabled' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordWeakPasswordEnabled
    $Stats.'Weak Passwords Disabled' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordWeakPasswordDisabled
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword)) {$Stats.'Weak Passwords (HASH)' = Get-ObjectCount -Object $DomainPasswordHashesWeakPassword}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) {$Stats.'Weak Passwords (HASH) Enabled' = Get-ObjectCount -Object $DomainPasswordHashesWeakPasswordEnabled}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {$Stats.'Weak Passwords (HASH) Disabled' = Get-ObjectCount -Object $DomainPasswordHashesWeakPasswordDisabled}
    $Stats.'Default Computer Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDefaultComputerPassword
    $Stats.'Password Not Required' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordPasswordNotRequired
    $Stats.'Password Never Expires' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordPasswordNeverExpires
    $Stats.'AES Keys Missing' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordAESKeysMissing
    $Stats.'PreAuth Not Required' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordPreAuthNotRequired
    $Stats.'DES Encryption Only' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDESEncryptionOnly
    $Stats.'Delegatable Admins' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDelegatableAdmins
    $Stats.'Duplicate Password Users' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDuplicatePasswordGroups
    $Stats.'Duplicate Password Grouped' = Get-ObjectCount ($PasswordsQuality.DomainPasswordDuplicatePasswordGroups.'Duplicate Group' | Sort-Object -Unique)
    return $Stats
}
function Get-WinADDomainRIDs {
    [CmdletBinding()]
    param([Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation,
        [string] $Domain = $Env:USERDNSDOMAIN)
    if ($null -eq $DomainInformation) {$DomainInformation = Get-ADDomain -Server $Domain}
    $rID = [ordered] @{}
    $rID.'rIDs Master' = $DomainInformation.RIDMaster
    $Property = Get-ADObject "cn=rid manager$,cn=system,$($DomainInformation.DistinguishedName)" -Property RidAvailablePool -Server $rID.'rIDs Master'
    [int32]$totalSIDS = $($Property.RidAvailablePool) / ([math]::Pow(2, 32))
    [int64]$temp64val = $totalSIDS * ([math]::Pow(2, 32))
    [int32]$currentRIDPoolCount = $($Property.RidAvailablePool) - $temp64val
    [int64]$RidsRemaining = $totalSIDS - $currentRIDPoolCount
    $Rid.'rIDs Available Pool' = $Property.RidAvailablePool
    $rID.'rIDs Total SIDs' = $totalSIDS
    $rID.'rIDs Issued' = $CurrentRIDPoolCount
    $rID.'rIDs Remaining' = $RidsRemaining
    $rID.'rIDs Percentage' = if ($RidsRemaining -eq 0) {$RidsRemaining.ToString("P")} else {($currentRIDPoolCount / $RidsRemaining * 100).ToString("P")}
    return $rID
}
function Get-WinADDomainServersCount {
    [CmdletBinding()]
    param([Array] $DomainServers)
    $DomainServers | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'N/A'}}} , @{L = 'System Count'; Expression = {$_.Count}}
}
function Get-WinADDomainServers {
    [CmdletBinding()]
    param([Array] $DomainComputersAll)
    foreach ($_ in $DomainComputersAll) {if ($_.OperatingSystem -like 'Windows Server*') {$_}}
}
function Get-WinADDomainTrusts {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [string] $DomainPDC,
        [Array] $Trusts)
    if ($null -eq $Trusts) {$Trusts = Get-ADTrust -Server $Domain -Filter * -Properties *}
    if ($DomainPDC -eq '') {$DomainPDC = (Get-ADDomain -Server $Domain).PDCEmulator}
    $PropertiesTrustWMI = @('FlatName',
        'SID',
        'TrustAttributes',
        'TrustDirection',
        'TrustedDCName',
        'TrustedDomain',
        'TrustIsOk',
        'TrustStatus',
        'TrustStatusString',
        'TrustType')
    $TrustStatatuses = Get-CimInstance -ClassName Microsoft_DomainTrustStatus -Namespace root\MicrosoftActiveDirectory -ComputerName $DomainPDC -ErrorAction SilentlyContinue -Verbose:$false -Property $PropertiesTrustWMI
    $ReturnData = foreach ($Trust in $Trusts) {
        $TrustWMI = $TrustStatatuses | & {process {if ($_.TrustedDomain -eq $Trust.Target) {$_}}}
        [PSCustomObject][ordered] @{'Trust Source' = $Domain
            'Trust Target' = $Trust.Target
            'Trust Direction' = $Trust.Direction
            'Trust Attributes' = if ($Trust.TrustAttributes -is [int]) {Set-TrustAttributes -Value $Trust.TrustAttributes} else {'Error - needs fixing'}
            'Trust Status' = if ($null -ne $TrustWMI) {$TrustWMI.TrustStatusString} else {'N/A'}
            'Forest Transitive' = $Trust.ForestTransitive
            'Selective Authentication' = $Trust.SelectiveAuthentication
            'SID Filtering Forest Aware' = $Trust.SIDFilteringForestAware
            'SID Filtering Quarantined' = $Trust.SIDFilteringQuarantined
            'Disallow Transivity' = $Trust.DisallowTransivity
            'Intra Forest' = $Trust.IntraForest
            'Tree Parent?' = $Trust.IsTreeParent
            'Tree Root?' = $Trust.IsTreeRoot
            'TGTDelegation' = $Trust.TGTDelegation
            'TrustedPolicy' = $Trust.TrustedPolicy
            'TrustingPolicy' = $Trust.TrustingPolicy
            'TrustType' = $Trust.TrustType
            'UplevelOnly' = $Trust.UplevelOnly
            'UsesAESKeys' = $Trust.UsesAESKeys
            'UsesRC4Encryption' = $Trust.UsesRC4Encryption
            'Trust Source DC' = if ($null -ne $TrustWMI) {$TrustWMI.PSComputerName} else {''}
            'Trust Target DC' = if ($null -ne $TrustWMI) {$TrustWMI.TrustedDCName.Replace('\\', '')} else {''}
            'Trust Source DN' = $Trust.Source
            'ObjectGUID' = $Trust.ObjectGUID
            'Created' = $Trust.Created
            'Modified' = $Trust.Modified
            'Deleted' = $Trust.Deleted
            'SID' = $Trust.securityIdentifier
            'TrustOK' = if ($null -ne $TrustWMI) {$TrustWMI.TrustIsOK} else {$false}
            'TrustStatus' = if ($null -ne $TrustWMI) {$TrustWMI.TrustStatus} else {-1}
        }
    }
    return $ReturnData
}
function Get-WinADDomainTrustsClean {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    Get-ADTrust -Server $Domain -Filter * -Properties * -ErrorAction SilentlyContinue
}
function Get-WinADDomainUsersAll {
    [CmdletBinding()]
    param([Array] $Users,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [HashTable] $DomainObjects,
        [string] $Splitter)
    [DateTime] $CurrentDate = Get-Date
    $UserList = foreach ($U in $Users) {
        $Manager = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $U.Manager
        [PsCustomObject][Ordered] @{'Name' = $U.Name
            'UserPrincipalName' = $U.UserPrincipalName
            'SamAccountName' = $U.SamAccountName
            'DisplayName' = $U.DisplayName
            'GivenName' = $U.GivenName
            'Surname' = $U.Surname
            'EmailAddress' = $U.EmailAddress
            'PasswordExpired' = $U.PasswordExpired
            'PasswordLastSet' = $U.PasswordLastSet
            'PasswordLastChanged(Days)' = if ($null -ne $U.PasswordLastSet) {"$(-$($U.PasswordLastSet - $CurrentDate).Days)"} else {}
            'PasswordNotRequired' = $U.PasswordNotRequired
            'PasswordNeverExpires' = $U.PasswordNeverExpires
            'Enabled' = $U.Enabled
            'Manager' = $Manager.Name
            'ManagerEmail' = if ($Splitter -ne '') {$Manager.EmailAddress -join $Splitter} else {$Manager.EmailAddress}
            'DateExpiry' = Convert-ToDateTime -Timestring $($U."msDS-UserPasswordExpiryTimeComputed")
            "DaysToExpire" = (Convert-TimeToDays -StartTime $CurrentDate -EndTime (Convert-ToDateTime -Timestring $($U."msDS-UserPasswordExpiryTimeComputed")))
            "AccountExpirationDate" = $U.AccountExpirationDate
            "AccountLockoutTime" = $U.AccountLockoutTime
            "AllowReversiblePasswordEncryption" = $U.AllowReversiblePasswordEncryption
            "BadLogonCount" = $U.BadLogonCount
            "CannotChangePassword" = $U.CannotChangePassword
            "CanonicalName" = $U.CanonicalName
            "Description" = $U.Description
            "DistinguishedName" = $U.DistinguishedName
            "EmployeeID" = $U.EmployeeID
            "EmployeeNumber" = $U.EmployeeNumber
            "LastBadPasswordAttempt" = $U.LastBadPasswordAttempt
            "LastLogonDate" = $U.LastLogonDate
            "Created" = $U.Created
            "Modified" = $U.Modified
            "Protected" = $U.ProtectedFromAccidentalDeletion
            "Primary Group" = (Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $U.PrimaryGroup -Type 'SamAccountName')
            "Member Of" = (Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $U.MemberOf -Type 'SamAccountName' -Splitter $Splitter)
            "Domain" = $Domain
        }
    }
    return $UserList
}
function Get-WinADDomainUsersAllFiltered {
    [CmdletBinding()]
    param([Array] $DomainUsers)
    foreach ($_ in $DomainUsers) {if ($_.PasswordNotRequired -eq $False) {$_}}
}
function Get-WinADDomainUsersExpiredExclDisabled {
    [CmdletBinding()]
    param([Array] $DomainUsers)
    foreach ($_ in $DomainUsers) {if ($_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false) {$_}}
}
function Get-WinADDomainUsersExpiredInclDisabled {
    [CmdletBinding()]
    param([Array] $DomainUsers)
    foreach ($_ in $DomainUsers) {if ($_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.PasswordNotRequired -eq $false) {$_}}
}
function Get-WinADDomainUsersFullList {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [switch] $Extended,
        [Array] $ForestSchemaUsers,
        [HashTable] $DomainObjects,
        [int] $ResultPageSize = 500000)
    if ($Extended) {[string] $Properties = '*'} else {
        $Properties = @('Name'
            'UserPrincipalName'
            'SamAccountName'
            'DisplayName'
            'GivenName'
            'Surname'
            'EmailAddress'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNotRequired'
            'PasswordNeverExpires'
            'Enabled'
            'Manager'
            'msDS-UserPasswordExpiryTimeComputed'
            'AccountExpirationDate'
            'AccountLockoutTime'
            'AllowReversiblePasswordEncryption'
            'BadLogonCount'
            'CannotChangePassword'
            'CanonicalName'
            'Description'
            'DistinguishedName'
            'EmployeeID'
            'EmployeeNumber'
            'LastBadPasswordAttempt'
            'LastLogonDate'
            'Created'
            'Modified'
            'ProtectedFromAccidentalDeletion'
            'PrimaryGroup'
            'MemberOf'
            if ($ForestSchemaUsers.Name -contains 'ExtensionAttribute1') {
                'ExtensionAttribute1'
                'ExtensionAttribute2'
                'ExtensionAttribute3'
                'ExtensionAttribute4'
                'ExtensionAttribute5'
                'ExtensionAttribute6'
                'ExtensionAttribute7'
                'ExtensionAttribute8'
                'ExtensionAttribute9'
                'ExtensionAttribute10'
                'ExtensionAttribute11'
                'ExtensionAttribute12'
                'ExtensionAttribute13'
                'ExtensionAttribute14'
                'ExtensionAttribute15'
            })
    }
    $Users = Get-ADUser -Server $Domain -ResultPageSize $ResultPageSize -Filter * -Properties $Properties
    foreach ($_ in $Users) {$DomainObjects.$($_.DistinguishedName) = $_}
    $Users
}
function Get-WinADDomainUsersNeverExpiring {
    [CmdletBinding()]
    param([Array] $DomainUsers)
    foreach ($_ in $DomainUsers) {if ($_.PasswordNeverExpires -eq $true -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false) {$_}}
}
function Get-WinADDomainUsersNeverExpiringInclDisabled {
    [CmdletBinding()]
    param([Array] $DomainUsers)
    foreach ($_ in $DomainUsers) {if ($_.PasswordNeverExpires -eq $true -and $_.PasswordNotRequired -eq $false) {$_}}
}
function Get-WinADDomainUsersSystemAccounts {
    [CmdletBinding()]
    param([Array] $DomainUsers)
    foreach ($_ in $DomainUsers) {if ($_.PasswordNotRequired -eq $true) {$_}}
}
function Get-WinADForest {
    [CmdletBinding()]
    param()
    try {Get-ADForest -ErrorAction Stop} catch {$null}
}
function Get-WinADForestFSMO {
    [CmdletBinding()]
    param([PSCustomObject] $Forest)
    [ordered] @{'Domain Naming Master' = $Forest.DomainNamingMaster
        'Schema Master' = $Forest.SchemaMaster
    }
}
function Get-WinADForestInfo {
    [CmdletBinding()]
    param([PSCustomObject] $Forest,
        [Microsoft.ActiveDirectory.Management.ADRootDSE] $RootDSE)
    [ordered] @{'Name' = $Forest.Name
        'Root Domain' = $Forest.RootDomain
        'Forest Distingushed Name' = $RootDSE.defaultNamingContext
        'Forest Functional Level' = $Forest.ForestMode
        'Domains Count' = ($Forest.Domains).Count
        'Sites Count' = ($Forest.Sites).Count
        'Domains' = ($Forest.Domains) -join ", "
        'Sites' = ($Forest.Sites) -join ", "
    }
}
function Get-WinADForestInformation {
    [CmdletBinding()]
    param ([PSWinDocumentation.ActiveDirectory[]] $TypesRequired,
        [switch] $RequireTypes,
        [string] $PathToPasswords,
        [string] $PathToPasswordsHashes,
        [switch] $PasswordQuality,
        [switch] $DontRemoveSupportData,
        [switch] $DontRemoveEmpty,
        [string] $Splitter,
        [switch] $Parallel,
        [int] $ResultPageSize = 500000)
    Write-Verbose -Message "Getting all information - Start"
    Write-Verbose -Message "Getting forest information - Start"
    $TimeToGenerateForest = Start-TimeLog
    if ($null -eq $TypesRequired) {
        Write-Verbose 'Getting forest information - TypesRequired is null. Getting all.'
        $TypesRequired = Get-Types -Types ([PSWinDocumentation.ActiveDirectory])
    }
    $Forest = Get-WinADForest
    if ($null -eq $Forest) {
        Write-Warning "Getting forest information - Failed to get information. This may mean that RSAT is not available or you can't connect to Active Directory."
        return
    }
    $Data = [ordered] @{}
    $Data.ForestRootDSE = Get-DataInformation -Text 'Getting forest information - ForestRootDSE' {Get-WinADRootDSE} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestRootDSE
        [PSWinDocumentation.ActiveDirectory]::ForestInformation)
    $Data.ForestInformation = Get-DataInformation -Text 'Getting forest information - Forest' {Get-WinADForestInfo -Forest $Forest -RootDSE $Data.ForestRootDSE} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestInformation)
    $Data.ForestSchemaPropertiesComputers = Get-DataInformation -Text "Getting forest information - ForestSchemaPropertiesComputers" {Get-WinADForestSchemaPropertiesComputers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSchemaPropertiesComputers
        [PSWinDocumentation.ActiveDirectory]::DomainComputersFullList
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAll
        [PSWinDocumentation.ActiveDirectory]::DomainComputersAllCount
        [PSWinDocumentation.ActiveDirectory]::DomainServers
        [PSWinDocumentation.ActiveDirectory]::DomainServersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputers
        [PSWinDocumentation.ActiveDirectory]::DomainComputersCount
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown
        [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknownCount
        [PSWinDocumentation.ActiveDirectory]::DomainBitlocker
        [PSWinDocumentation.ActiveDirectory]::DomainLAPS)
    $Data.ForestSchemaPropertiesUsers = Get-DataInformation -Text "Getting forest information - ForestSchemaPropertiesUsers" {Get-WinADForestSchemaPropertiesUsers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSchemaPropertiesUsers
        [PSWinDocumentation.ActiveDirectory]::DomainUsersFullList)
    $Data.ForestUPNSuffixes = Get-DataInformation -Text 'Getting forest information - ForestUPNSuffixes' {Get-WinADForestUPNSuffixes -Forest $Forest} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestUPNSuffixes)
    $Data.ForestSPNSuffixes = Get-DataInformation -Text 'Getting forest information - ForestSPNSuffixes' {Get-WinADForestSPNSuffixes -Forest $Forest} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSPNSuffixes)
    $Data.ForestGlobalCatalogs = Get-DataInformation -Text 'Getting forest information - ForestGlobalCatalogs' {$Forest.GlobalCatalogs} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestGlobalCatalogs)
    $Data.ForestFSMO = Get-DataInformation -Text 'Getting forest information - ForestFSMO' {Get-WinADForestFSMO -Forest $Forest} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestFSMO)
    $Data.ForestDomainControllers = Get-DataInformation -Text 'Getting forest information - ForestDomainControllers' {Get-WinADForestControllers} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestDomainControllers)
    $Data.ForestSites = Get-DataInformation -Text 'Getting forest information - ForestSites' {Get-WinADForestSites} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSites
        [PSWinDocumentation.ActiveDirectory]::ForestSites1
        [PSWinDocumentation.ActiveDirectory]::ForestSites2)
    $Data.ForestSites1 = Get-DataInformation -Text 'Getting forest information - ForestSites1' {Get-WinADForestSites1 -ForestSites $Data.ForestSites} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSites1)
    $Data.ForestSites2 = Get-DataInformation -Text 'Getting forest information - ForestSites2' {Get-WinADForestSites2 -ForestSites $Data.ForestSites} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSites2)
    $Data.ForestSubnets = Get-DataInformation -Text 'Getting forest information - ForestSubnets' {Get-WinADForestSubnets} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSubnets
        [PSWinDocumentation.ActiveDirectory]::ForestSubnets1
        [PSWinDocumentation.ActiveDirectory]::ForestSubnets2)
    $Data.ForestSubnets1 = Get-DataInformation -Text 'Getting forest information - ForestSubnets1' {Get-WinADForestSubnets1 -ForestSubnets $Data.ForestSubnets} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSubnets1)
    $Data.ForestSubnets2 = Get-DataInformation -Text 'Getting forest information - ForestSubnets2' {Get-WinADForestSubnets2 -ForestSubnets $Data.ForestSubnets} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSubnets2)
    $Data.ForestSiteLinks = Get-DataInformation -Text 'Getting forest information - ForestSiteLinks' {Get-WinADForestSiteLinks} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSiteLinks)
    $Data.ForestOptionalFeatures = Get-DataInformation -Text 'Getting forest information - ForestOptionalFeatures' {Get-WinADForestOptionalFeatures} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestOptionalFeatures)
    $EndTimeForest = Stop-TimeLog -Time $TimeToGenerateForest -Continue
    $Data.FoundDomains = Get-DataInformation -Text 'Getting forest information - Domains' {$FoundDomains = @{}
        foreach ($Domain in $Forest.Domains) {$FoundDomains.$Domain = Get-WinADDomainInformation -Domain $Domain -TypesRequired $TypesRequired -PathToPasswords $PathToPasswords -PathToPasswordsHashes $PathToPasswordsHashes -ForestSchemaComputers $Data.ForestSchemaPropertiesComputers -ForestSchemaUsers $Data.ForestSchemaPropertiesUsers -PasswordQuality:$PasswordQuality -Splitter $Splitter -Parallel:$Parallel -ResultPageSize $ResultPageSize}
        $FoundDomains} -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory].GetEnumValues() | Where-Object {$_ -like 'Domain*'})
    $EndTimeAll = Stop-TimeLog -Time $TimeToGenerateForest
    Clear-DataInformation -Data $Data -TypesRequired $TypesRequired -DontRemoveSupportData:$DontRemoveSupportData -DontRemoveEmpty:$DontRemoveEmpty
    Write-Verbose "Getting forest information - Stop - Time to generate: $EndTimeForest"
    Write-Verbose "Getting all information - Stop - Time to generate: $EndTimeAll"
    return $Data
}
function Get-WinADForestOptionalFeatures {
    [CmdletBinding()]
    param()
    $OptionalFeatures = $(Get-ADOptionalFeature -Filter *)
    $Optional = [ordered]@{'Recycle Bin Enabled' = 'N/A'
        'Privileged Access Management Feature Enabled' = 'N/A'
    }
    foreach ($Feature in $OptionalFeatures) {
        if ($Feature.Name -eq 'Recycle Bin Feature') {$Optional.'Recycle Bin Enabled' = $Feature.EnabledScopes.Count -gt 0}
        if ($Feature.Name -eq 'Privileged Access Management Feature') {$Optional.'Privileged Access Management Feature Enabled' = $Feature.EnabledScopes.Count -gt 0}
    }
    return $Optional
}
function Get-WinADForestSchemaPropertiesComputers {
    [CmdletBinding()]
    param()
    $Schema = [directoryservices.activedirectory.activedirectoryschema]::GetCurrentSchema()
    @($Schema.FindClass("computer").mandatoryproperties | Select-Object name, commonname, description, syntax
        $Schema.FindClass("computer").optionalproperties | Select-Object name, commonname, description, syntax)
}
function Get-WinADForestSchemaPropertiesUsers {
    [CmdletBinding()]
    param()
    $Schema = [directoryservices.activedirectory.activedirectoryschema]::GetCurrentSchema()
    @($Schema.FindClass("user").mandatoryproperties | Select-Object name, commonname, description, syntax
        $Schema.FindClass("user").optionalproperties | Select-Object name, commonname, description, syntax)
}
function Get-WinADForestSiteLinks {
    [CmdletBinding()]
    param()
    $ExludedProperties = @('PropertyNames', 'AddedProperties', 'RemovedProperties', 'ModifiedProperties', 'PropertyCount')
    $Properties = @('Name', 'Cost', 'ReplicationFrequencyInMinutes', 'ReplInterval',
        'ReplicationSchedule', 'Created', 'Modified', 'Deleted', 'InterSiteTransportProtocol',
        'DistinguishedName', 'ProtectedFromAccidentalDeletion')
    return Get-ADReplicationSiteLink -Filter * -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExludedProperties
}
function Get-WinADForestSites {
    [CmdletBinding()]
    param()
    $ExludedProperties = @('PropertyNames', 'AddedProperties', 'RemovedProperties', 'ModifiedProperties', 'PropertyCount')
    $Properties = @('Name',
        'DisplayName', 'Description', 'CanonicalName', 'DistinguishedName', 'Location', 'ManagedBy', 'Created', 'Modified', 'Deleted',
        'ProtectedFromAccidentalDeletion', 'RedundantServerTopologyEnabled',
        'AutomaticInterSiteTopologyGenerationEnabled',
        'AutomaticTopologyGenerationEnabled',
        'Subnets',
        'sDRightsEffective',
        'TopologyCleanupEnabled',
        'TopologyDetectStaleEnabled',
        'TopologyMinimumHopsEnabled',
        'UniversalGroupCachingEnabled',
        'UniversalGroupCachingRefreshSite',
        'WindowsServer2000BridgeheadSelectionMethodEnabled',
        'WindowsServer2000KCCISTGSelectionBehaviorEnabled',
        'WindowsServer2003KCCBehaviorEnabled',
        'WindowsServer2003KCCIgnoreScheduleEnabled',
        'WindowsServer2003KCCSiteLinkBridgingEnabled')
    return Get-ADReplicationSite -Filter * -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExludedProperties
}
function Get-WinADForestSites1 {
    [CmdletBinding()]
    param([Array] $ForestSites)
    @(foreach ($Sites in $ForestSites) {
            [PSCustomObject][ordered] @{'Name' = $Sites.Name
                'Description' = $Sites.Description
                'Protected' = $Sites.ProtectedFromAccidentalDeletion
                'Modified' = $Sites.Modified
                'Created' = $Sites.Created
                'Deleted' = $Sites.Deleted
            }
        })
}
function Get-WinADForestSites2 {
    [CmdletBinding()]
    param([Array] $ForestSites)
    @(foreach ($Sites in $ForestSites) {
            [PSCustomObject][ordered] @{'Name' = $Sites.Name
                'Topology Cleanup Enabled' = $Sites.TopologyCleanupEnabled
                'Topology Detect Stale Enabled' = $Sites.TopologyDetectStaleEnabled
                'Topology Minimum Hops Enabled' = $Sites.TopologyMinimumHopsEnabled
                'Universal Group Caching Enabled' = $Sites.UniversalGroupCachingEnabled
                'Universal Group Caching RefreshSite' = $Sites.UniversalGroupCachingRefreshSite
            }
        })
}
function Get-WinADForestSPNSuffixes {
    [CmdletBinding()]
    param([PSCustomObject] $Forest)
    @(foreach ($SPN in $Forest.SPNSuffixes) {[PSCustomObject] @{Name = $SPN}
        })
}
function Get-WinADForestSubnets {
    [CmdletBinding()]
    param()
    $ExludedProperties = @('PropertyNames', 'AddedProperties', 'RemovedProperties', 'ModifiedProperties', 'PropertyCount')
    $Properties = @('Name', 'DisplayName', 'Description', 'Site', 'ProtectedFromAccidentalDeletion', 'Created', 'Modified', 'Deleted')
    return Get-ADReplicationSubnet -Filter * -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExludedProperties
}
function Get-WinADForestSubnets1 {
    [CmdletBinding()]
    param([Array] $ForestSubnets)
    foreach ($Subnets in $ForestSubnets) {
        [PSCustomObject][ordered] @{'Name' = $Subnets.Name
            'Description' = $Subnets.Description
            'Protected' = $Subnets.ProtectedFromAccidentalDeletion
            'Modified' = $Subnets.Modified
            'Created' = $Subnets.Created
            'Deleted' = $Subnets.Deleted
        }
    }
}
function Get-WinADForestSubnets2 {
    param([Array] $ForestSubnets)
    @(foreach ($Subnets in $ForestSubnets) {
            [PSCustomObject][ordered] @{'Name' = $Subnets.Name
                'Site' = $Subnets.Site
            }
        })
}
function Get-WinADForestUPNSuffixes {
    param([PSCustomObject] $Forest)
    @([PSCustomObject] @{Name = $Forest.RootDomain
            Type = 'Primary / Default UPN'
        }
        foreach ($UPN in $Forest.UPNSuffixes) {
            [PSCustomObject] @{Name = $UPN
                Type = 'Secondary'
            }
        })
}
function Get-WinADKerberosUnconstrainedDelegation {
    param()
    Get-ADObject -filter {(UserAccountControl -BAND 0x0080000) -OR (UserAccountControl -BAND 0x1000000) -OR (msDS-AllowedToDelegateTo -like '*')} -Properties Name, ObjectClass, PrimaryGroupID, UserAccountControl, ServicePrincipalName, msDS-AllowedToDelegateTo
}
function Get-WinADRootDSE {
    [CmdletBinding()]
    param([string] $Domain = ($Env:USERDNSDOMAIN).ToLower())
    try {if ($Domain -ne '') {Get-ADRootDSE -Properties * -Server $Domain} else {Get-ADRootDSE -Properties *}} catch {Write-Warning "Getting forest/domain information - $Domain RootDSE Error: $($_.Error)"}
}
function Get-WinGroupMembers {
    [CmdletBinding()]
    param([Array] $Groups,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [ValidateSet("Recursive", "Standard")][String] $Option,
        [hashtable] $DomainObjects,
        [string] $Splitter)
    [DateTime] $CurrentDate = Get-Date
    if ($Option -eq 'Recursive') {
        [Array] $GroupMembersRecursive = foreach ($Group in $Groups) {
            try {$GroupMembership = Get-ADGroupMember -Server $Domain -Identity $Group.'Group SID' -Recursive -ErrorAction Stop} catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Warning "Couldn't get information about group $($Group.Name) with SID $($Group.'Group SID') error: $ErrorMessage"
                continue
            }
            foreach ($Member in $GroupMembership) {
                $Object = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $Member.DistinguishedName
                $Manager = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $Object.Manager
                [PSCustomObject][ordered] @{'Group Name' = $Group.'Group Name'
                    'Group SID' = $Group.'Group SID'
                    'Group Category' = $Group.'Group Category'
                    'Group Scope' = $Group.'Group Scope'
                    'High Privileged Group' = if ($Group.adminCount -eq 1) {$True} else {$False}
                    'Display Name' = $Object.DisplayName
                    'Name' = $Member.Name
                    'User Principal Name' = $Object.UserPrincipalName
                    'Sam Account Name' = $Object.SamAccountName
                    'Email Address' = $Object.EmailAddress
                    'PasswordExpired' = $Object.PasswordExpired
                    'PasswordLastSet' = $Object.PasswordLastSet
                    'PasswordNotRequired' = $Object.PasswordNotRequired
                    'PasswordNeverExpires' = $Object.PasswordNeverExpires
                    'Enabled' = $Object.Enabled
                    'SID' = $Member.SID.Value
                    'Manager' = $Manager.Name
                    'ManagerEmail' = if ($Splitter -ne '') {$Manager.EmailAddress -join $Splitter} else {$Manager.EmailAddress}
                    'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")
                    "DaysToExpire" = (Convert-TimeToDays -StartTime $CurrentDate -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                    "AccountExpirationDate" = $Object.AccountExpirationDate
                    "AccountLockoutTime" = $Object.AccountLockoutTime
                    "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                    "BadLogonCount" = $Object.BadLogonCount
                    "CannotChangePassword" = $Object.CannotChangePassword
                    "CanonicalName" = $Object.CanonicalName
                    'Given Name' = $Object.GivenName
                    'Surname' = $Object.Surname
                    "Description" = $Object.Description
                    "DistinguishedName" = $Object.DistinguishedName
                    "EmployeeID" = $Object.EmployeeID
                    "EmployeeNumber" = $Object.EmployeeNumber
                    "LastBadPasswordAttempt" = $Object.LastBadPasswordAttempt
                    "LastLogonDate" = $Object.LastLogonDate
                    "Created" = $Object.Created
                    "Modified" = $Object.Modified
                    "Protected" = $Object.ProtectedFromAccidentalDeletion
                    "Domain" = $Domain
                }
            }
        }
        if ($GroupMembersRecursive.Count -eq 1) {return , $GroupMembersRecursive}
        return $GroupMembersRecursive
    }
    if ($Option -eq 'Standard') {
        [Array] $GroupMembersDirect = foreach ($Group in $Groups) {
            foreach ($Member in $Group.'Group Members DN') {
                $Object = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $Member
                $Manager = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $Object.Manager
                [PSCustomObject][ordered] @{'Group Name' = $Group.'Group Name'
                    'Group SID' = $Group.'Group SID'
                    'Group Category' = $Group.'Group Category'
                    'Group Scope' = $Group.'Group Scope'
                    'DisplayName' = $Object.DisplayName
                    'High Privileged Group' = if ($Group.adminCount -eq 1) {$True} else {$False}
                    'UserPrincipalName' = $Object.UserPrincipalName
                    'SamAccountName' = $Object.SamAccountName
                    'EmailAddress' = $Object.EmailAddress
                    'PasswordExpired' = $Object.PasswordExpired
                    'PasswordLastSet' = $Object.PasswordLastSet
                    'PasswordNotRequired' = $Object.PasswordNotRequired
                    'PasswordNeverExpires' = $Object.PasswordNeverExpires
                    'Enabled' = $Object.Enabled
                    'Manager' = $Manager.Name
                    'ManagerEmail' = if ($Splitter -ne '') {$Manager.EmailAddress -join $Splitter} else {$Manager.EmailAddress}
                    'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")
                    "DaysToExpire" = (Convert-TimeToDays -StartTime $CurrentDate -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                    "AccountExpirationDate" = $Object.AccountExpirationDate
                    "AccountLockoutTime" = $Object.AccountLockoutTime
                    "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                    "BadLogonCount" = $Object.BadLogonCount
                    "CannotChangePassword" = $Object.CannotChangePassword
                    "CanonicalName" = $Object.CanonicalName
                    "Description" = $Object.Description
                    "DistinguishedName" = $Object.DistinguishedName
                    "EmployeeID" = $Object.EmployeeID
                    "EmployeeNumber" = $Object.EmployeeNumber
                    "LastBadPasswordAttempt" = $Object.LastBadPasswordAttempt
                    "LastLogonDate" = $Object.LastLogonDate
                    'Name' = $Object.Name
                    'SID' = $Object.SID.Value
                    'GivenName' = $Object.GivenName
                    'Surname' = $Object.Surname
                    "Created" = $Object.Created
                    "Modified" = $Object.Modified
                    "Protected" = $Object.ProtectedFromAccidentalDeletion
                    "Domain" = $Domain
                }
            }
        }
        if ($GroupMembersDirect.Count -eq 1) {return , $GroupMembersDirect}
        return $GroupMembersDirect
    }
}
function Get-WinGroups {
    [CmdletBinding()]
    param ([Array] $Groups,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [string] $Splitter,
        [hashtable] $DomainObjects)
    $ReturnGroups = foreach ($Group in $Groups) {
        $Manager = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $Group.ManagedBy
        [PsCustomObject][ordered] @{'Group Name' = $Group.Name
            'Group Category' = $Group.GroupCategory
            'Group Scope' = $Group.GroupScope
            'Group SID' = $Group.SID.Value
            'High Privileged Group' = if ($Group.adminCount -eq 1) {$True} else {$False}
            'Member Count' = $Group.Members.Count
            'MemberOf Count' = $Group.MemberOf.Count
            'Manager' = $Manager.Name
            'Manager Email' = if ($Splitter -ne '') {$Manager.EmailAddress -join $Splitter} else {$Manager.EmailAddress}
            'Group Members' = Get-ADObjectFromDNHash -ADCatalog $DomainObjects -DistinguishedName $Group.Members -Splitter $Splitter -Type 'SamAccountName'
            'Group Members DN' = $Group.Members
            "Domain" = $Domain
        }
    }
    return $ReturnGroups
}
Function Set-TrustAttributes {
    [cmdletbinding()]
    Param([parameter(Mandatory = $false, ValueFromPipeline = $True)][int32]$Value)
    [String[]]$TrustAttributes = @(Foreach ($V in $Value) {
            if ([int32]$V -band 0x00000001) {"Non Transitive"}
            if ([int32]$V -band 0x00000002) {"UpLevel"}
            if ([int32]$V -band 0x00000004) {"Quarantaine (SID Filtering enabled)"}
            if ([int32]$V -band 0x00000008) {"Forest Transitive"}
            if ([int32]$V -band 0x00000010) {"Cross Organization (Selective Authentication enabled)"}
            if ([int32]$V -band 0x00000020) {"Within Forest"}
            if ([int32]$V -band 0x00000040) {"Treat as External"}
            if ([int32]$V -band 0x00000080) {"Uses RC4 Encryption"}
        })
    return $TrustAttributes
}
Export-ModuleMember -Function @('Get-WinADDomainInformation', 'Get-WinADForestInformation') -Alias @()