PSWinDocumentation.AD.psm1

Add-Type -TypeDefinition @"
public enum ActiveDirectory {
    // Forest Information - Section Main
    ForestInformation,
    ForestFSMO,
    ForestGlobalCatalogs,
    ForestOptionalFeatures,
    ForestUPNSuffixes,
    ForestSPNSuffixes,
    ForestSites,
    ForestSites1,
    ForestSites2,
    ForestSubnets,
    ForestSubnets1,
    ForestSubnets2,
    ForestSiteLinks,
    ForestDomainControllers,
 
    // 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,
    DomainOrganizationalUnitsExtended,
    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 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) {
            $ADObject = foreach ($_ in $Catalog) {if ($_.DistinguishedName -eq $Object) {$_}}
            if ($ADObject) {if ($Type -eq '') {$ADObject} else {$ADObject.$Type}}
        }
    }
    if ($Splitter) {return ($FoundObjects | Sort-Object) -join $Splitter} else {return $FoundObjects | Sort-Object}
}
function Get-WinADAccounts {
    [CmdletBinding()]
    param([Object] $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)
    $Time = Start-TimeLog
    Write-Verbose 'Getting forest information - Forest'
    try {Get-ADDomain -Server $Domain -ErrorAction Stop} catch {$null}
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting forest information - Forest Time: $EndTime"
}
function Get-WinADDomainBitlocker {
    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) {
        $Bitlockers = Get-ADObject -Filter 'objectClass -eq "msFVE-RecoveryInformation"' -SearchBase $Computer.DistinguishedName -Properties 'WhenCreated', 'msFVE-RecoveryPassword'
        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-WinADDomainComputersFullList {
    [cmdletbinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $ForestSchemaComputers)
    Write-Verbose "Getting domain information - $Domain DomainComputersFullList"
    $TimeUsers = Start-TimeLog
    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'
            if ($ForestSchemaComputers.Name -contains 'ms-Mcs-AdmPwd') {
                'ms-Mcs-AdmPwd'
                'ms-Mcs-AdmPwdExpirationTime'
            })
    }
    Get-ADComputer -Server $Domain -Filter * -ResultPageSize 500000 -Properties $Properties -ErrorAction SilentlyContinue
    $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainComputersFullList Time: $EndUsers"
}
function Get-WinADDomainDNSData {
    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-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,
        [Array] $DomainUsersFullList,
        [Array] $DomainGroupsFullList)
    $PolicyUsers = foreach ($Policy in $DomainFineGrainedPolicies) {
        $Users = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $U}
        $Groups = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDistingusishedName -ADCatalog $DomainGroupsFullList -DistinguishedName $U}
        foreach ($User in $Users) {
            [pscustomobject][ordered] @{'Policy Name' = $Policy.Name
                Name = $User.Name
                SamAccountName = $User.SamAccountName
                Type = $User.ObjectClass
                SID = $User.SID
            }
        }
        foreach ($Group in $Groups) {
            [pscustomobject][ordered] @{'Policy Name' = $Policy.Name
                Name = $Group.Name
                SamAccountName = $Group.SamAccountName
                Type = $Group.ObjectClass
                SID = $Group.SID
            }
        }
    }
    return $PolicyUsers
}
function Get-WinADDomainFineGrainedPoliciesUsersExtended {
    [CmdletBinding()]
    param([Array] $DomainFineGrainedPolicies,
        [Array] $DomainUsersFullList,
        [Array] $DomainGroupsFullList,
        [string] $Domain = ($Env:USERDNSDOMAIN).ToLower())
    $Time = Start-TimeLog
    Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsersExtended"
    $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
                    }
                }
            }
        })
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsersExtended Time: $EndTime"
    return $PolicyUsers
}
function Get-WinADDomainFSMO {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation)
    Write-Verbose "Getting domain information - $Domain DomainFSMO"
    $Time = Start-TimeLog
    [ordered] @{'PDC Emulator' = $DomainInformation.PDCEmulator
        'RID Master' = $DomainInformation.RIDMaster
        'Infrastructure Master' = $DomainInformation.InfrastructureMaster
    }
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainFSMO Time: $EndTime"
}
function Get-WinADDomainGroupsFullList {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    Write-Verbose "Getting domain information - $Domain DomainGroupsFullList"
    $TimeUsers = Start-TimeLog
    [string[]] $Properties = '*'
    [string[]] $ExcludeProperty = '*Certificate', 'PropertyNames', '*Properties', 'PropertyCount', 'Certificates', 'nTSecurityDescriptor'
    Get-ADGroup -Server $Domain -Filter * -ResultPageSize 500000 -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExcludeProperty
    $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainGroupsFullList Time: $EndUsers"
}
function Get-DomainGroupsPriviliged {
    [cmdletbinding()]
    param([Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation,
        $DomainGroups)
    $Time = Start-TimeLog
    Write-Verbose "Getting domain information - $Domain DomainGroupsPriviliged"
    $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') {$_}}
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainGroupsPriviliged Time: $EndTime"
}
function Get-WinADDomainGUIDs {
    [cmdletbinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Microsoft.ActiveDirectory.Management.ADEntity] $RootDSE)
    $Time = Start-TimeLog
    if ($null -eq $RootDSE) {$RootDSE = Get-ADRootDSE -Server $Domain}
    Write-Verbose "Getting domain information - $Domain DomainGUIDS"
    $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)}}
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainGUIDS Time: $EndTime"
    return $GUID
}
function Get-WinADDomainInformation {
    [CmdletBinding()]
    param ([string] $Domain,
        [ActiveDirectory[]] $TypesRequired,
        [string] $PathToPasswords,
        [string] $PathToPasswordsHashes,
        [switch] $Extended,
        [Array] $ForestSchemaComputers,
        [Array] $ForestSchemaUsers)
    if ([string]::IsNullOrEmpty($Domain)) {
        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 ([ActiveDirectory])
    }
    $TimeToGenerate = Start-TimeLog
    if ($null -eq $ForestSchemaComputers) {$ForestSchemaComputers = Get-WinADForestSchemaPropertiesComputers}
    if ($null -eq $ForestSchemaUsers) {$ForestSchemaUsers = Get-WinADForestSchemaPropertiesUsers}
    $Data = [ordered] @{}
    $Data.DomainRootDSE = Get-WinADRootDSE -Domain $Domain
    Write-Verbose "Getting domain information - $Domain DomainInformation"
    $Data.DomainInformation = Get-WinADDomain -Domain $Domain
    if ($null -eq $Data.DomainInformation -or $null -eq $Data.DomainRootDSE) {return}
    $Data.DomainGroupsFullList = Get-WinADDomainGroupsFullList -Domain $Domain
    $Data.DomainUsersFullList = Get-WinADDomainUsersFullList -Domain $Domain -Extended:$Extended -ForestSchemaUsers $ForestSchemaUsers
    $Data.DomainComputersFullList = Get-WinADDomainComputersFullList -Domain $Domain -ForestSchemaComputers $ForestSchemaComputers
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainComputersAll,
            [ActiveDirectory]::DomainComputersAllCount,
            [ActiveDirectory]::DomainServers,
            [ActiveDirectory]::DomainServersCount,
            [ActiveDirectory]::DomainComputers,
            [ActiveDirectory]::DomainComputersCount,
            [ActiveDirectory]::DomainComputersUnknown,
            [ActiveDirectory]::DomainComputersUnknownCount)) {
        Write-Verbose "Getting domain information - $Domain DomainComputersAll"
        $Data.DomainComputersAll = $Data.DomainComputersFullList | Select-Object SamAccountName, Enabled, OperatingSystem, PasswordLastSet, IPv4Address, IPv6Address, Name, DNSHostName, ManagedBy, OperatingSystemVersion, OperatingSystemHotfix, OperatingSystemServicePack , PasswordNeverExpires, PasswordNotRequired, UserPrincipalName, LastLogonDate, LockedOut, LogonCount, CanonicalName, SID, Created, Modified, Deleted, MemberOf
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersAllCount) {
        Write-Verbose "Getting domain information - $Domain DomainComputersAllCount"
        $Data.DomainComputersAllCount = $Data.DomainComputersAll | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'System Count'; Expression = {$_.Count}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainServers) {
        Write-Verbose "Getting domain information - $Domain DomainServers"
        $Data.DomainServers = $Data.DomainComputersAll | & {process {if ($_.OperatingSystem -like 'Windows Server*') {$_}}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainServersCount) {
        Write-Verbose "Getting domain information - $Domain DomainServersCount"
        $Data.DomainServersCount = $Data.DomainServers | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'N/A'}}} , @{L = 'System Count'; Expression = {$_.Count}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputers) {
        Write-Verbose "Getting domain information - $Domain DomainComputers"
        $Data.DomainComputers = $Data.DomainComputersAll | & {process {if ($_.OperatingSystem -notlike 'Windows Server*' -and $null -ne $_.OperatingSystem) {$_}}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersCount) {
        Write-Verbose "Getting domain information - $Domain DomainComputersCount"
        $Data.DomainComputersCount = $Data.DomainComputers | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'N/A'}}} , @{L = 'System Count'; Expression = {$_.Count}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersUnknown) {
        Write-Verbose "Getting domain information - $Domain DomainComputersUnknown"
        $Data.DomainComputersUnknown = $Data.DomainComputersAll | & {process {if ($null -eq $_.OperatingSystem) {$_}}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersUnknownCount) {
        Write-Verbose "Getting domain information - $Domain DomainComputersUnknownCount"
        $Data.DomainComputersUnknownCount = $Data.DomainComputersUnknown | Group-Object -Property OperatingSystem | Select-Object @{L = 'System Name'; Expression = {if ($_.Name -ne '') {$_.Name} else {'Unknown'}}} , @{L = 'System Count'; Expression = {$_.Count}}
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainRIDs) {$Data.DomainRIDs = Get-WinADDomainRIDs -DomainInformation $Data.DomainInformation -Domain $Domain}
    if (Find-TypesNeeded -TypesRequired $TypesRequired @([ActiveDirectory]::DomainGUIDS, [ActiveDirectory]::DomainOrganizationalUnitsBasicACL, [ActiveDirectory]::DomainOrganizationalUnitsExtended)) {$Data.DomainGUIDS = Get-WinADDomainGUIDs -RootDSE $Data.DomainRootDSE -Domain $Domain}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAuthenticationPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainAuthenticationPolicies"
        $Data.DomainAuthenticationPolicies = $(Get-ADAuthenticationPolicy -Server $Domain -LDAPFilter '(name=AuthenticationPolicy*)')
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAuthenticationPolicySilos)) {
        Write-Verbose "Getting domain information - $Domain DomainAuthenticationPolicySilos"
        $Data.DomainAuthenticationPolicySilos = $(Get-ADAuthenticationPolicySilo -Server $Domain -Filter 'Name -like "*AuthenticationPolicySilo*"')
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainCentralAccessPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainCentralAccessPolicies"
        $Data.DomainCentralAccessPolicies = $(Get-ADCentralAccessPolicy -Server $Domain -Filter *)
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainCentralAccessRules)) {
        Write-Verbose "Getting domain information - $Domain DomainCentralAccessRules"
        $Data.DomainCentralAccessRules = $(Get-ADCentralAccessRule -Server $Domain -Filter *)
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainClaimTransformPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainClaimTransformPolicies"
        $Data.DomainClaimTransformPolicies = $(Get-ADClaimTransformPolicy -Server $Domain -Filter *)
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainClaimTypes)) {
        Write-Verbose "Getting domain information - $Domain DomainClaimTypes"
        $Data.DomainClaimTypes = $(Get-ADClaimType -Server $Domain -Filter *)
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainDNSSRV, [ActiveDirectory]::DomainDNSA)) {
        Write-Verbose "Getting domain information - $Domain DomainDNSSRV / DomainDNSA"
        $Data.DomainDNSData = Get-WinADDomainDNSData -Domain $Domain
        $Data.DomainDNSSrv = $Data.DomainDNSData.SRV
        $Data.DomainDNSA = $Data.DomainDNSData.A
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFSMO, [ActiveDirectory]::DomainTrusts, [ActiveDirectory]::DomainTrustsClean)) {$Data.DomainFSMO = Get-WinADDomainFSMO -Domain $Domain -DomainInformation $Data.DomainInformation}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainTrustsClean, [ActiveDirectory]::DomainTrusts)) {$Data.DomainTrustsClean = Get-WinADDomainTrustsClean -Domain $Domain}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainTrusts)) {$Data.DomainTrusts = Get-WinADDomainTrusts -DomainPDC $Data.DomainFSMO.'PDC Emulator' -Trusts $Data.DomainTrustsClean -Domain $Domain}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupPolicies,
            [ActiveDirectory]::DomainGroupPoliciesDetails,
            [ActiveDirectory]::DomainGroupPoliciesACL)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupPolicies"
        $Data.DomainGroupPoliciesClean = $(Get-GPO -Domain $Domain -All)
        $Data.DomainGroupPolicies = foreach ($gpo in $Data.DomainGroupPoliciesClean) {
            [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
            }
        }
        $Data.DomainGroupPoliciesDetails = Invoke-Command -ScriptBlock {Write-Verbose -Message "Getting domain information - $Domain Group Policies Details"
            $Output = ForEach ($GPO in $Data.DomainGroupPoliciesClean) {
                [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' = $XmlGPReport.GPO.SecurityDescriptor.SDDL.'#text'
                }
            }
            return $Output}
        $Data.DomainGroupPoliciesACL = Invoke-Command -ScriptBlock {Write-Verbose -Message "Getting domain information - $Domain Group Policies ACLs"
            $Output = ForEach ($GPO in $Data.DomainGroupPoliciesClean) {
                [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}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainBitlocker)) {}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainLAPS)) {$Data.DomainLAPS = Get-WinADDomainLAPS -Domain $Domain -Computers $Data.DomainComputersFullList}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainDefaultPasswordPolicy)) {
        Write-Verbose -Message "Getting domain information - $Domain DomainDefaultPasswordPolicy"
        $Data.DomainDefaultPasswordPolicy = Invoke-Command -ScriptBlock {$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
            }}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainOrganizationalUnits,
            [ActiveDirectory]::DomainContainers,
            [ActiveDirectory]::DomainOrganizationalUnitsDN,
            [ActiveDirectory]::DomainOrganizationalUnitsACL,
            [ActiveDirectory]::DomainOrganizationalUnitsBasicACL,
            [ActiveDirectory]::DomainOrganizationalUnitsExtended)) {
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnits Clean"
        $Data.DomainOrganizationalUnitsClean = $(Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter *)
        $Data.DomainOrganizationalUnits = Get-WinADDomainOrganizationalUnits -Domain $Domain -OrgnaizationalUnits $Data.DomainOrganizationalUnitsClean
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsDN"
        $Data.DomainOrganizationalUnitsDN = @($Data.DomainInformation.DistinguishedName
            $Data.DomainOrganizationalUnitsClean.DistinguishedName
            $Data.DomainContainers.DistinguishedName)
        $Data.DomainOrganizationalUnitsBasicACL = Get-WinADDomainOrganizationalUnitsACL -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean -Domain $Domain -NetBiosName $Data.DomainInformation.NetBIOSName -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext
        $Data.DomainOrganizationalUnitsExtended = Get-WinADDomainOrganizationalUnitsACLExtended -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean -Domain $Domain -NetBiosName $Data.DomainInformation.NetBIOSName -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext -GUID $Data.DomainGUIDS
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainUsers,
            [ActiveDirectory]::DomainUsersAll,
            [ActiveDirectory]::DomainUsersSystemAccounts,
            [ActiveDirectory]::DomainUsersNeverExpiring,
            [ActiveDirectory]::DomainUsersNeverExpiringInclDisabled,
            [ActiveDirectory]::DomainUsersExpiredInclDisabled,
            [ActiveDirectory]::DomainUsersExpiredExclDisabled,
            [ActiveDirectory]::DomainUsersCount)) {
        $Data.DomainUsers = Invoke-Command -ScriptBlock {Write-Verbose "Getting domain information - $Domain DomainUsers"
            return Get-WinUsers -Users $Data.DomainUsersFullList -Domain $Domain -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -ADCatalogUsers $Data.DomainUsersFullList}
        Write-Verbose "Getting domain information - $Domain DomainUsersAll"
        $Data.DomainUsersAll = $Data.DomainUsers | Where-Object {$_.PasswordNotRequired -eq $False}
        Write-Verbose "Getting domain information - $Domain DomainUsersSystemAccounts"
        $Data.DomainUsersSystemAccounts = $Data.DomainUsers | Where-Object {$_.PasswordNotRequired -eq $true}
        Write-Verbose "Getting domain information - $Domain DomainUsersNeverExpiring"
        $Data.DomainUsersNeverExpiring = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $true -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false}
        Write-Verbose "Getting domain information - $Domain DomainUsersNeverExpiringInclDisabled"
        $Data.DomainUsersNeverExpiringInclDisabled = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $true -and $_.PasswordNotRequired -eq $false}
        Write-Verbose "Getting domain information - $Domain DomainUsersExpiredInclDisabled"
        $Data.DomainUsersExpiredInclDisabled = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.PasswordNotRequired -eq $false}
        Write-Verbose "Getting domain information - $Domain DomainUsersExpiredExclDisabled"
        $Data.DomainUsersExpiredExclDisabled = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false}
        Write-Verbose "Getting domain information - $Domain All Users Count"
        $Data.DomainUsersCount = [ordered] @{'Users Count Incl. System' = Get-ObjectCount -Object $Data.DomainUsers
            'Users Count' = Get-ObjectCount -Object $Data.DomainUsersAll
            'Users Expired' = Get-ObjectCount -Object $Data.DomainUsersExpiredExclDisabled
            'Users Expired Incl. Disabled' = Get-ObjectCount -Object $Data.DomainUsersExpiredInclDisabled
            'Users Never Expiring' = Get-ObjectCount -Object $Data.DomainUsersNeverExpiring
            'Users Never Expiring Incl. Disabled' = Get-ObjectCount -Object $Data.DomainUsersNeverExpiringInclDisabled
            'Users System Accounts' = Get-ObjectCount -Object $Data.DomainUsersSystemAccounts
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainControllers)) {
        Write-Verbose "Getting domain information - $Domain DomainControllers"
        $Data.DomainControllersClean = $(Get-ADDomainController -Server $Domain -Filter *)
        $Data.DomainControllers = Invoke-Command -ScriptBlock {foreach ($Policy in $Data.DomainControllersClean) {
                [PSCustomObject][ordered] @{'Name' = $Policy.Name
                    'Host Name' = $Policy.HostName
                    'Operating System' = $Policy.OperatingSystem
                    'Site' = $Policy.Site
                    'Ipv4' = $Policy.Ipv4Address
                    'Ipv6' = $Policy.Ipv6Address
                    'Global Catalog?' = $Policy.IsGlobalCatalog
                    'Read Only?' = $Policy.IsReadOnly
                    'Ldap Port' = $Policy.LdapPort
                    'SSL Port' = $Policy.SSLPort
                }
            }}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFineGrainedPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainFineGrainedPolicies"
        $Data.DomainFineGrainedPolicies = Get-WinADDomainFineGrainedPolicies -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFineGrainedPoliciesUsers)) {
        Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsers"
        $Data.DomainFineGrainedPoliciesUsers = Get-WinADDomainFineGrainedPoliciesUsers -DomainFineGrainedPolicies $Data.DomainFineGrainedPolicies -DomainUsersFullList $Data.DomainUsersFullList -DomainGroupsFullList $Data.DomainGroupsFullList
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended)) {$Data.DomainFineGrainedPoliciesUsersExtended = Get-WinADDomainFineGrainedPoliciesUsersExtended -DomainFineGrainedPolicies $Data.DomainFineGrainedPoliciesUsers -DomainUsersFullList $Data.DomainUsersFullList -DomainGroupsFullList $Data.DomainGroupsFullList -Domain $Domain}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroups, [ActiveDirectory]::DomainGroupsSpecial)) {
        Write-Verbose "Getting domain information - $Domain DomainGroups"
        $Data.DomainGroups = Get-WinGroups -Groups $Data.DomainGroupsFullList -Users $Data.DomainUsersFullList -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroups, [ActiveDirectory]::DomainGroupsMembers)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsMembers"
        $Data.DomainGroupsMembers = Get-WinGroupMembers -Groups $Data.DomainGroups -Domain $Domain -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -ADCatalogUsers $Data.DomainUsersFullList -Option Standard
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroups, [ActiveDirectory]::DomainGroupsMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsMembersRecursive"
        $Data.DomainGroupsMembersRecursive = Get-WinGroupMembers -Groups $Data.DomainGroups -Domain $Domain -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -ADCatalogUsers $Data.DomainUsersFullList -Option Recursive
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsPriviliged)) {$Data.DomainGroupsPriviliged = Get-DomainGroupsPriviliged -DomainGroups $Data.DomainGroups -DomainInformation $Data.DomainInformation}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsSpecial, [ActiveDirectory]::DomainGroupMembersRecursiveSpecial)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsSpecial"
        $Data.DomainGroupsSpecial = $Data.DomainGroups | Where-Object {($_.'Group SID').Length -eq 12}
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsSpecialMembers, [ActiveDirectory]::DomainGroupsSpecialMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupMembersSpecialRecursive"
        $Data.DomainGroupsSpecialMembers = $Data.DomainGroupsMembers | Where-Object {($_.'Group SID').Length -eq 12} | Select-Object *
        Write-Verbose "Getting domain information - $Domain DomainGroupsSpecialMembersRecursive"
        $Data.DomainGroupsSpecialMembersRecursive = $Data.DomainGroupsMembersRecursive | Where-Object {($_.'Group SID').Length -eq 12} | Select-Object *
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsPriviligedMembers, [ActiveDirectory]::DomainGroupsPriviligedMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsPriviligedMembers"
        $Data.DomainGroupsPriviligedMembers = $Data.DomainGroupsMembers | Where-Object {$Data.DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID')} | Select-Object *
        Write-Verbose "Getting domain information - $Domain DomainGroupsPriviligedMembersRecursive"
        $Data.DomainGroupsPriviligedMembersRecursive = $Data.DomainGroupsMembersRecursive | Where-Object {$Data.DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID')} | Select-Object *
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAdministrators, [ActiveDirectory]::DomainGroupsMembers)) {
        Write-Verbose "Getting domain information - $Domain DomainAdministrators"
        $Data.DomainAdministrators = $Data.DomainGroupsMembers | Where-Object {$_.'Group SID' -eq $('{0}-512' -f $Data.DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAdministratorsRecursive, [ActiveDirectory]::DomainGroupsMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainAdministratorsRecursive"
        $Data.DomainAdministratorsRecursive = $Data.DomainGroupsMembersRecursive | Where-Object {$_.'Group SID' -eq $('{0}-512' -f $Data.DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainEnterpriseAdministrators, [ActiveDirectory]::DomainGroupsMembers)) {
        Write-Verbose "Getting domain information - $Domain DomainEnterpriseAdministrators"
        $Data.DomainEnterpriseAdministrators = $Data.DomainGroupsMembers | Where-Object {$_.'Group SID' -eq $('{0}-519' -f $Data.DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainEnterpriseAdministratorsRecursive, [ActiveDirectory]::DomainGroupsMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainEnterpriseAdministratorsRecursive"
        $Data.DomainEnterpriseAdministratorsRecursive = $Data.DomainGroupsMembersRecursive | Where-Object {$_.'Group SID' -eq $('{0}-519' -f $Data.DomainInformation.DomainSID.Value)} | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordDataUsers,
            [ActiveDirectory]::DomainPasswordDataPasswords,
            [ActiveDirectory]::DomainPasswordClearTextPassword,
            [ActiveDirectory]::DomainPasswordLMHash,
            [ActiveDirectory]::DomainPasswordEmptyPassword,
            [ActiveDirectory]::DomainPasswordWeakPassword,
            [ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordList,
            [ActiveDirectory]::DomainPasswordDefaultComputerPassword,
            [ActiveDirectory]::DomainPasswordPasswordNotRequired,
            [ActiveDirectory]::DomainPasswordPasswordNeverExpires,
            [ActiveDirectory]::DomainPasswordAESKeysMissing,
            [ActiveDirectory]::DomainPasswordPreAuthNotRequired,
            [ActiveDirectory]::DomainPasswordDESEncryptionOnly,
            [ActiveDirectory]::DomainPasswordDelegatableAdmins,
            [ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
            [ActiveDirectory]::DomainPasswordStats,
            [ActiveDirectory]::DomainPasswordHashesWeakPassword,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataUsers - This will take a while if set!"
        $TimeToProcess = Start-TimeLog
        try {$Data.DomainPasswordDataUsers = Get-ADReplAccount -All -Server $Data.DomainInformation.DnsRoot -NamingContext $Data.DomainInformation.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"}
        }
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataUsers - Time: $($TimeToProcess | Stop-TimeLog)"
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordDataPasswords,
            [ActiveDirectory]::DomainPasswordClearTextPassword,
            [ActiveDirectory]::DomainPasswordLMHash,
            [ActiveDirectory]::DomainPasswordEmptyPassword,
            [ActiveDirectory]::DomainPasswordWeakPassword,
            [ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordList,
            [ActiveDirectory]::DomainPasswordDefaultComputerPassword,
            [ActiveDirectory]::DomainPasswordPasswordNotRequired,
            [ActiveDirectory]::DomainPasswordPasswordNeverExpires,
            [ActiveDirectory]::DomainPasswordAESKeysMissing,
            [ActiveDirectory]::DomainPasswordPreAuthNotRequired,
            [ActiveDirectory]::DomainPasswordDESEncryptionOnly,
            [ActiveDirectory]::DomainPasswordDelegatableAdmins,
            [ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
            [ActiveDirectory]::DomainPasswordStats,
            [ActiveDirectory]::DomainPasswordHashesWeakPassword,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswords - This will take a while if set!"
        Write-Verbose "Getting domain password information - $Domain Passwords Path: $PathToPasswords"
        $TimeToProcess = Start-TimeLog
        $Data.DomainPasswordDataPasswords = Get-WinADDomainPasswordQuality -FilePath $PathToPasswords -DomainInformation $Data -Verbose:$false -PasswordQualityUsers $Data.DomainPasswordDataUsers
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswords - Time: $($TimeToProcess | Stop-TimeLog)"
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPassword,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswordsHashes - This will take a while if set!"
        Write-Verbose "Getting domain password information - $Domain Passwords Hashes Path: $PathToPasswordsHashes"
        $TimeToProcess = Start-TimeLog
        $Data.DomainPasswordDataPasswordsHashes = Get-WinADDomainPasswordQuality -FilePath $PathToPasswordsHashes -DomainInformation $Data -UseHashes -Verbose:$false -PasswordQualityUsers $Data.DomainPasswordDataUsers
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswordsHashes - Time: $($TimeToProcess | Stop-TimeLog)"
    }
    if ($Data.DomainPasswordDataPasswords) {$PasswordsQuality = $Data.DomainPasswordDataPasswords} elseif ($Data.DomainPasswordDataPasswordsHashes) {$PasswordsQuality = $Data.DomainPasswordDataPasswordsHashes} else {$PasswordsQuality = $null}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordClearTextPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordClearTextPassword"
        $Data.DomainPasswordClearTextPassword = $PasswordsQuality.DomainPasswordClearTextPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordLMHash)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordLMHash"
        $Data.DomainPasswordLMHash = $PasswordsQuality.DomainPasswordLMHash
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordEmptyPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordEmptyPassword"
        $Data.DomainPasswordEmptyPassword = $PasswordsQuality.DomainPasswordEmptyPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordWeakPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPassword"
        $Data.DomainPasswordWeakPassword = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordWeakPasswordEnabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordEnabled"
        $Data.DomainPasswordWeakPasswordEnabled = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordEnabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordDisabled"
        $Data.DomainPasswordWeakPasswordDisabled = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordDisabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordWeakPasswordList)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordList"
        $Data.DomainPasswordWeakPasswordList = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordList
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordDefaultComputerPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDefaultComputerPassword"
        $Data.DomainPasswordDefaultComputerPassword = $PasswordsQuality.DomainPasswordDefaultComputerPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordPasswordNotRequired)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordPasswordNotRequired"
        $Data.DomainPasswordPasswordNotRequired = $PasswordsQuality.DomainPasswordPasswordNotRequired
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordPasswordNeverExpires)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordPasswordNeverExpires"
        $Data.DomainPasswordPasswordNeverExpires = $PasswordsQuality.DomainPasswordPasswordNeverExpires
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordAESKeysMissing)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordAESKeysMissing"
        $Data.DomainPasswordAESKeysMissing = $PasswordsQuality.DomainPasswordAESKeysMissing
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordPreAuthNotRequired)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordPreAuthNotRequired"
        $Data.DomainPasswordPreAuthNotRequired = $PasswordsQuality.DomainPasswordPreAuthNotRequired
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordDESEncryptionOnly)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDESEncryptionOnly"
        $Data.DomainPasswordDESEncryptionOnly = $PasswordsQuality.DomainPasswordDESEncryptionOnly
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordDelegatableAdmins)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDelegatableAdmins"
        $Data.DomainPasswordDelegatableAdmins = $PasswordsQuality.DomainPasswordDelegatableAdmins
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordDuplicatePasswordGroups)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDuplicatePasswordGroups"
        $Data.DomainPasswordDuplicatePasswordGroups = $PasswordsQuality.DomainPasswordDuplicatePasswordGroups
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPassword"
        $Data.DomainPasswordHashesWeakPassword = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPasswordEnabled"
        $Data.DomainPasswordHashesWeakPasswordEnabled = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordEnabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPasswordDisabled"
        $Data.DomainPasswordHashesWeakPasswordDisabled = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordDisabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordStats)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordStats"
        $Data.DomainPasswordStats = Invoke-Command -ScriptBlock {$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 @([ActiveDirectory]::DomainPasswordHashesWeakPassword)) {$Stats.'Weak Passwords (HASH)' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPassword}
            if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) {$Stats.'Weak Passwords (HASH) Enabled' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPasswordEnabled}
            if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {$Stats.'Weak Passwords (HASH) Disabled' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.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}
    }
    $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)
    $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'
            'Laps Password' = $Computer.'ms-Mcs-AdmPwd'
            'Laps Expire (days)' = Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime'))
            'Laps Expiration Time' = Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime')
            'DistinguishedName' = $Computer.'DistinguishedName'
        }
    }
}
function Get-WinADDomainOrganizationalUnits {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $OrgnaizationalUnits)
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnits"
    if ($null -eq $OrgnaizationalUnits) {$OrgnaizationalUnits = $(Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter *)}
    $TimeOU = Start-TimeLog
    $Output = foreach ($O in $OrgnaizationalUnits) {
        [PSCustomObject] @{'Canonical Name' = $O.CanonicalName
            'Managed By' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $_.ManagedBy -Verbose).Name
            'Manager Email' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $_.ManagedBy -Verbose).EmailAddress
            'Protected' = $O.ProtectedFromAccidentalDeletion
            Description = $O.Description
            Created = $O.Created
            Modified = $O.Modified
            Deleted = $O.Deleted
            'Postal Code' = $O.PostalCode
            City = $O.City
            Country = $O.Country
            State = $O.State
            'Street Address' = $O.StreetAddress
            DistinguishedName = $O.DistinguishedName
            ObjectGUID = $O.ObjectGUID
        }
    }
    $Output | Sort-Object 'Canonical Name'
    $EndOU = Stop-TimeLog -Time $TimeOU -Option OneLiner
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnits Time: $EndOU"
}
function Get-WinADDomainOrganizationalUnitsACL {
    [cmdletbinding()]
    param($DomainOrganizationalUnitsClean,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [string] $NetBiosName,
        [string] $RootDomainNamingContext)
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL"
    $Time = Start-TimeLog
    $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
            }
        })
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL Time: $EndTime"
}
function Get-WinADDomainOrganizationalUnitsACLExtended {
    [cmdletbinding()]
    param($DomainOrganizationalUnitsClean,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [string] $NetBiosName,
        [string] $RootDomainNamingContext,
        $GUID)
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended"
    $Time = Start-TimeLog
    $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
                }
            }
        })
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended Time: $EndTime"
}
function Get-WinADDomainPasswordQuality {
    [CmdletBinding()]
    param ($DomainInformation,
        $PasswordQualityUsers,
        [string] $FilePath,
        [switch] $UseHashes)
    if ([string]::IsNullOrEmpty($FilePath)) {
        Write-Verbose "Get-WinADDomainPasswordQuality - File path not given, using hashes set to $UseHashes"
        return $null
    }
    if (-not (Test-Path -Path $FilePath)) {
        Write-Verbose "Get-WinADDomainPasswordQuality - File path doesn't exists, using hashes set to $UseHashes"
        return $null
    }
    if ($null -eq $DomainInformation) {
        Write-Verbose "Get-WinADDomainPasswordQuality - No DomainInformation given, no alternative approach either. Terminating password quality check."
        return $null
    }
    $Data = [ordered] @{}
    if ($PasswordQualityUsers) {$Data.PasswordQualityUsers = $PasswordQualityUsers} else {$Data.PasswordQualityUsers = Get-ADReplAccount -All -Server $DomainInformation.DomainInformation.DnsRoot -NamingContext $DomainInformation.DomainInformation.DistinguishedName}
    $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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll, $DomainInformation.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 $DomainInformation.DomainUsersAll) {if ($_.SamAccountName -eq $User) {$_}}
                $FullComputerInformation = foreach ($_ in $DomainInformation.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-WinADDomainRIDs {
    [CmdletBinding()]
    param([Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation,
        [string] $Domain = $Env:USERDNSDOMAIN)
    $Time = Start-TimeLog
    Write-Verbose "Getting domain information - $Domain DomainRIDs"
    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")}
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainRIDs Time: $EndTime"
    return $rID
}
function Get-WinADDomainTrusts {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [string] $DomainPDC,
        [Array] $Trusts)
    Write-Verbose "Getting domain information - $Domain DomainTrusts"
    $Time = Start-TimeLog
    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 {'N/A'}
            'Trust Target DC' = if ($null -ne $TrustWMI) {$TrustWMI.TrustedDCName.Replace('\\', '')} else {'N/A'}
            '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}
        }
    }
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainTrusts Time: $EndTime"
    return $ReturnData
}
function Get-WinADDomainTrustsClean {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $TypesRequired)
    Write-Verbose "Getting domain information - $Domain DomainTrustsClean"
    $Time = Start-TimeLog
    Get-ADTrust -Server $Domain -Filter * -Properties * -ErrorAction SilentlyContinue
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainTrustsClean Time: $EndTime"
}
function Get-WinADDomainUsersFullList {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN,
        [switch] $Extended,
        [Array] $ForestSchemaUsers)
    Write-Verbose "Getting domain information - $Domain DomainUsersFullList"
    $TimeUsers = Start-TimeLog
    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'
            })
    }
    [string[]] $ExcludeProperty = '*Certificate', 'PropertyNames', '*Properties', 'PropertyCount', 'Certificates', 'nTSecurityDescriptor'
    Get-ADUser -Server $Domain -ResultPageSize 500000 -Filter * -Properties $Properties
    $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainUsersFullList Time: $EndUsers"
}
function Get-WinADForest {
    [CmdletBinding()]
    param()
    $Time = Start-TimeLog
    Write-Verbose 'Getting forest information - Forest'
    try {Get-ADForest -ErrorAction Stop} catch {$null}
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting forest information - Forest Time: $EndTime"
}
function Get-WinADForestInfo {
    [CmdletBinding()]
    param([PSCustomObject] $Forest)
    $Time = Start-TimeLog
    Write-Verbose 'Getting forest information - Forest Information'
    [ordered] @{'Name' = $Forest.Name
        'Root Domain' = $Forest.RootDomain
        'Forest Functional Level' = $Forest.ForestMode
        'Domains Count' = ($Forest.Domains).Count
        'Sites Count' = ($Forest.Sites).Count
        'Domains' = ($Forest.Domains) -join ", "
        'Sites' = ($Forest.Sites) -join ", "
    }
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting forest information - Forest Information Time: $EndTime"
}
function Get-WinADForestInformation {
    [CmdletBinding()]
    param ([Object] $TypesRequired,
        [switch] $RequireTypes,
        [string] $PathToPasswords,
        [string] $PathToPasswordsHashes)
    Write-Verbose "Getting all information"
    $TimeToGenerateForest = Start-TimeLog
    if ($null -eq $TypesRequired) {
        Write-Verbose 'Get-WinADForestInformation - TypesRequired is null. Getting all.'
        $TypesRequired = Get-Types -Types ([ActiveDirectory])
    }
    $Data = [ordered] @{}
    $Data.Forest = Get-WinADForest
    if ($null -eq $Data.Forest) {return}
    $Data.RootDSE = Get-WinADRootDSE
    Write-Verbose 'Getting forest information - ForestName & ForestNameDN & Domains list'
    $Data.ForestName = $Data.Forest.Name
    $Data.ForestNameDN = $Data.RootDSE.defaultNamingContext
    $Data.Domains = $Data.Forest.Domains
    $Data.ForestSchemaPropertiesComputers = Get-WinADForestSchemaPropertiesComputers
    $Data.ForestSchemaPropertiesUsers = Get-WinADForestSchemaPropertiesUsers
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestInformation)) {$Data.ForestInformation = Get-WinADForestInfo -Forest $Data.Forest}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestUPNSuffixes)) {
        Write-Verbose 'Getting forest information - Forest UPNSuffixes'
        $Data.ForestUPNSuffixes = Get-WinADForestUPNSuffixes -Forest $Data.Forest
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestGlobalCatalogs)) {
        Write-Verbose 'Getting forest information - Forest GlobalCatalogs'
        $Data.ForestGlobalCatalogs = $Data.Forest.GlobalCatalogs
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSPNSuffixes)) {
        Write-Verbose 'Getting forest information - Forest SPNSuffixes'
        $Data.ForestSPNSuffixes = $Data.Forest.SPNSuffixes
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestFSMO)) {
        Write-Verbose 'Getting forest information - Forest FSMO'
        $Data.ForestFSMO = [ordered] @{'Domain Naming Master' = $Data.Forest.DomainNamingMaster
            'Schema Master' = $Data.Forest.SchemaMaster
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestDomainControllers)) {$Data.ForestDomainControllers = Get-WinADForestControllers}
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSites, [ActiveDirectory]::ForestSites1, [ActiveDirectory]::ForestSites2)) {
        Write-Verbose 'Getting forest information - Forest Sites'
        $Data.ForestSites = Get-WinADForestSites
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSites1)) {
        Write-Verbose 'Getting forest information - Forest Sites1'
        $Data.ForestSites1 = Get-WinADForestSites1 -ForestSites $Data.ForestSites
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSites2)) {
        Write-Verbose 'Getting forest information - Forest Sites2'
        $Data.ForestSites2 = Get-WinADForestSites2 -ForestSites $Data.ForestSites
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSubnet , [ActiveDirectory]::ForestSubnets1, [ActiveDirectory]::ForestSubnets2)) {
        Write-Verbose 'Getting forest information - Forest Subnets'
        $Data.ForestSubnets = Get-WinADForestSubnets
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSubnets1)) {
        Write-Verbose 'Getting forest information - Forest Subnets1'
        $Data.ForestSubnets1 = Get-WinADForestSubnets1 -ForestSubnets $Data.ForestSubnets
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSubnets2)) {
        Write-Verbose 'Getting forest information - Forest Subnets2'
        $Data.ForestSubnets2 = Get-WinADForestSubnets2 -ForestSubnets $Data.ForestSubnets
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSiteLinks)) {
        Write-Verbose 'Getting forest information - Forest SiteLinks'
        $Data.ForestSiteLinks = Get-WinADForestSiteLinks
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestOptionalFeatures)) {
        Write-Verbose 'Getting forest information - Forest Optional Features'
        $Data.ForestOptionalFeatures = Get-WinADForestOptionalFeatures
    }
    $EndTimeForest = Stop-TimeLog -Time $TimeToGenerateForest -Continue
    Write-Verbose "Getting forest information - Time to generate: $EndTimeForest"
    $Data.FoundDomains = [ordered]@{}
    foreach ($Domain in $Data.Domains) {$Data.FoundDomains.$Domain = Get-WinADDomainInformation -Domain $Domain -TypesRequired $TypesRequired -PathToPasswords $PathToPasswords -PathToPasswordsHashes $PathToPasswordsHashes -ForestSchemaComputers $Data.ForestSchemaPropertiesComputers -ForestSchemaUsers $Data.ForestSchemaPropertiesUsers}
    $EndTimeAll = Stop-TimeLog -Time $TimeToGenerateForest
    Write-Verbose "Getting all information - 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()
    Write-Verbose "Getting forest information - ForestSchemaPropertiesComputers"
    $Time = Start-TimeLog
    $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)
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - ForestSchemaPropertiesComputers Time: $EndTime"
}
function Get-WinADForestSchemaPropertiesUsers {
    [CmdletBinding()]
    param()
    Write-Verbose "Getting forest information - ForestSchemaPropertiesUsers"
    $Time = Start-TimeLog
    $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)
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - ForestSchemaPropertiesUsers Time: $EndTime"
}
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-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())
    $Time = Start-TimeLog
    try {
        if ($Domain -ne '') {
            Write-Verbose "Getting domain information - $Domain DomainRootDSE"
            Get-ADRootDSE -Properties * -Server $Domain
        } else {
            Write-Verbose 'Getting forest information - RootDSE'
            Get-ADRootDSE -Properties *
        }
    } catch {Write-Warning "Getting domain information - $Domain DomainRootDSE Error: $($_.Error)"}
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    if ($Domain -ne '') {Write-Verbose "Getting domain information - $Domain DomainRootDSE Time: $EndTime"} else {Write-Verbose "Getting forest information - RootDSE Time: $EndTime"}
}
function Get-WinGroupMembers {
    [CmdletBinding()]
    param([System.Object[]] $Groups,
        [string] $Domain = $Env:USERDNSDOMAIN,
        [System.Object[]] $ADCatalog,
        [System.Object[]] $ADCatalogUsers,
        [ValidateSet("Recursive", "Standard")][String] $Option)
    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-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $Member.DistinguishedName)
                [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' = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).Name
                    'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).EmailAddress
                    'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")
                    "DaysToExpire" = (Convert-TimeToDays -StartTime GET-DATE -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-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $Member)
                [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' = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).Name
                    'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).EmailAddress
                    'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")
                    "DaysToExpire" = (Convert-TimeToDays -StartTime GET-DATE -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 ([System.Object[]] $Groups,
        [System.Object[]] $Users,
        [string] $Domain = $Env:USERDNSDOMAIN)
    $ReturnGroups = foreach ($Group in $Groups) {
        $User = foreach ($_ in $Users) {if ($_.DistinguishedName -eq $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' = $User.Name
            'Manager Email' = $User.EmailAddress
            'Group Members' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -DistinguishedName $Group.Members -Type 'SamAccountName')
            'Group Members DN' = $Group.Members
            "Domain" = $Domain
        }
    }
    return $ReturnGroups
}
function Get-WinUsers {
    [CmdletBinding()]
    param([System.Object[]] $Users,
        [System.Object[]] $ADCatalog,
        [System.Object[]] $ADCatalogUsers,
        [string] $Domain = $Env:USERDNSDOMAIN)
    [DateTime] $CurrentDate = Get-Date
    $UserList = foreach ($U in $Users) {
        [PsCustomObject][Ordered] @{'Name' = $U.Name
            'UserPrincipalName' = $U.UserPrincipalName
            'SamAccountName' = $U.SamAccountName
            'Display Name' = $U.DisplayName
            'Given Name' = $U.GivenName
            'Surname' = $U.Surname
            'EmailAddress' = $U.EmailAddress
            'PasswordExpired' = $U.PasswordExpired
            'PasswordLastSet' = $U.PasswordLastSet
            'Password Last Changed' = if ($null -ne $U.PasswordLastSet) {"$(-$($U.PasswordLastSet - $CurrentDate).Days) days"} else {'N/A'}
            'PasswordNotRequired' = $U.PasswordNotRequired
            'PasswordNeverExpires' = $U.PasswordNeverExpires
            'Enabled' = $U.Enabled
            'Manager' = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $U.Manager).Name
            'Manager Email' = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $U.Manager).EmailAddress
            'DateExpiry' = Convert-ToDateTime -Timestring $($U."msDS-UserPasswordExpiryTimeComputed") -Verbose
            "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-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $U.PrimaryGroup -Type 'SamAccountName')
            "Member Of" = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $U.MemberOf -Type 'SamAccountName' -Splitter ', ')
            "Domain" = $Domain
        }
    }
    return $UserList
}
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 @()